blob: 89cf554c076a221bd1de4b74ba63d88afaff3bd3 [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
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070054
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055def _lwrite(path, content):
56 lock = '%s.lock' % path
57
Chirayu Desai303a82f2014-08-19 22:57:17 +053058 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 try:
60 fd.write(content)
61 finally:
62 fd.close()
63
64 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070065 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070066 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080067 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 raise
69
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
Shawn O. Pearce48244782009-04-16 08:25:57 -070071def _error(fmt, *args):
72 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070073 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
David Pursehousef33929d2015-08-24 14:39:14 +090076def _warn(fmt, *args):
77 msg = fmt % args
78 print('warn: %s' % msg, file=sys.stderr)
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081def not_rev(r):
82 return '^' + r
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080085def sq(r):
86 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080087
Jonathan Nieder93719792015-03-17 11:29:58 -070088_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
90
Jonathan Nieder93719792015-03-17 11:29:58 -070091def _ProjectHooks():
92 """List the hooks present in the 'hooks' directory.
93
94 These hooks are project hooks and are copied to the '.git/hooks' directory
95 of all subprojects.
96
97 This function caches the list of hooks (based on the contents of the
98 'repo/hooks' directory) on the first call.
99
100 Returns:
101 A list of absolute paths to all of the files in the hooks directory.
102 """
103 global _project_hook_list
104 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700105 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700106 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700107 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 return _project_hook_list
109
110
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700111class DownloadedChange(object):
112 _commit_cache = None
113
114 def __init__(self, project, base, change_id, ps_id, commit):
115 self.project = project
116 self.base = base
117 self.change_id = change_id
118 self.ps_id = ps_id
119 self.commit = commit
120
121 @property
122 def commits(self):
123 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700124 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
125 '--abbrev-commit',
126 '--pretty=oneline',
127 '--reverse',
128 '--date-order',
129 not_rev(self.base),
130 self.commit,
131 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700132 return self._commit_cache
133
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135class ReviewableBranch(object):
136 _commit_cache = None
137
138 def __init__(self, project, branch, base):
139 self.project = project
140 self.branch = branch
141 self.base = base
142
143 @property
144 def name(self):
145 return self.branch.name
146
147 @property
148 def commits(self):
149 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700150 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
151 '--abbrev-commit',
152 '--pretty=oneline',
153 '--reverse',
154 '--date-order',
155 not_rev(self.base),
156 R_HEADS + self.name,
157 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700176 def UploadForReview(self, people,
177 auto_topic=False,
178 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200179 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700180 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700190 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200192 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800193 validate_certs=validate_certs,
194 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700195
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700196 def GetPublishedRefs(self):
197 refs = {}
198 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700199 self.branch.remote.SshReviewUrl(self.project.UserEmail),
200 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700201 for line in output.split('\n'):
202 try:
203 (sha, ref) = line.split()
204 refs[sha] = ref
205 except ValueError:
206 pass
207
208 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700209
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700210
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213 def __init__(self, config):
214 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100215 self.project = self.printer('header', attr='bold')
216 self.branch = self.printer('header', attr='bold')
217 self.nobranch = self.printer('nobranch', fg='red')
218 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219
Anthony King7bdac712014-07-16 12:56:40 +0100220 self.added = self.printer('added', fg='green')
221 self.changed = self.printer('changed', fg='red')
222 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700223
224
225class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700226
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227 def __init__(self, config):
228 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100229 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
Anthony King7bdac712014-07-16 12:56:40 +0100232class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
James W. Mills24c13082012-04-12 15:04:13 -0500234 def __init__(self, name, value, keep):
235 self.name = name
236 self.value = value
237 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700239
Anthony King7bdac712014-07-16 12:56:40 +0100240class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243 self.src = src
244 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800245 self.abs_src = abssrc
246 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247
248 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800249 src = self.abs_src
250 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 # copy file if it does not exist or is out of date
252 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
253 try:
254 # remove existing file first, since it might be read-only
255 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800256 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400257 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200258 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700259 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700261 shutil.copy(src, dest)
262 # make the file read-only
263 mode = os.stat(dest)[stat.ST_MODE]
264 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
265 os.chmod(dest, mode)
266 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700267 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700269
Anthony King7bdac712014-07-16 12:56:40 +0100270class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Wink Saville4c426ef2015-06-03 08:05:17 -0700272 def __init__(self, git_worktree, src, dest, relsrc, absdest):
273 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 self.src = src
275 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700276 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 self.abs_dest = absdest
278
Wink Saville4c426ef2015-06-03 08:05:17 -0700279 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500280 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700281 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 try:
283 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800284 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800285 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700288 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500289 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700290 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500291 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700292 _error('Cannot link file %s to %s', relSrc, absDest)
293
294 def _Link(self):
295 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
296 on the src linking all of the files in the source in to the destination
297 directory.
298 """
299 # We use the absSrc to handle the situation where the current directory
300 # is not the root of the repo
301 absSrc = os.path.join(self.git_worktree, self.src)
302 if os.path.exists(absSrc):
303 # Entity exists so just a simple one to one link operation
304 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
305 else:
306 # Entity doesn't exist assume there is a wild card
307 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700308 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700309 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700310 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 else:
312 absSrcFiles = glob.glob(absSrc)
313 for absSrcFile in absSrcFiles:
314 # Create a releative path from source dir to destination dir
315 absSrcDir = os.path.dirname(absSrcFile)
316 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
317
318 # Get the source file name
319 srcFile = os.path.basename(absSrcFile)
320
321 # Now form the final full paths to srcFile. They will be
322 # absolute for the desintaiton and relative for the srouce.
323 absDest = os.path.join(absDestDir, srcFile)
324 relSrc = os.path.join(relSrcDir, srcFile)
325 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500326
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700327
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700328class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330 def __init__(self,
331 name,
Anthony King7bdac712014-07-16 12:56:40 +0100332 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700333 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100334 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700335 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700336 orig_name=None,
337 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700338 self.name = name
339 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700340 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700341 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100342 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700343 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700344 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700345
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700346
Doug Anderson37282b42011-03-04 11:54:18 -0800347class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349 """A RepoHook contains information about a script to run as a hook.
350
351 Hooks are used to run a python script before running an upload (for instance,
352 to run presubmit checks). Eventually, we may have hooks for other actions.
353
354 This shouldn't be confused with files in the 'repo/hooks' directory. Those
355 files are copied into each '.git/hooks' folder for each project. Repo-level
356 hooks are associated instead with repo actions.
357
358 Hooks are always python. When a hook is run, we will load the hook into the
359 interpreter and execute its main() function.
360 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700361
Doug Anderson37282b42011-03-04 11:54:18 -0800362 def __init__(self,
363 hook_type,
364 hooks_project,
365 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400366 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800367 abort_if_user_denies=False):
368 """RepoHook constructor.
369
370 Params:
371 hook_type: A string representing the type of hook. This is also used
372 to figure out the name of the file containing the hook. For
373 example: 'pre-upload'.
374 hooks_project: The project containing the repo hooks. If you have a
375 manifest, this is manifest.repo_hooks_project. OK if this is None,
376 which will make the hook a no-op.
377 topdir: Repo's top directory (the one containing the .repo directory).
378 Scripts will run with CWD as this directory. If you have a manifest,
379 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400380 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800381 abort_if_user_denies: If True, we'll throw a HookError() if the user
382 doesn't allow us to run the hook.
383 """
384 self._hook_type = hook_type
385 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400386 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800387 self._topdir = topdir
388 self._abort_if_user_denies = abort_if_user_denies
389
390 # Store the full path to the script for convenience.
391 if self._hooks_project:
392 self._script_fullpath = os.path.join(self._hooks_project.worktree,
393 self._hook_type + '.py')
394 else:
395 self._script_fullpath = None
396
397 def _GetHash(self):
398 """Return a hash of the contents of the hooks directory.
399
400 We'll just use git to do this. This hash has the property that if anything
401 changes in the directory we will return a different has.
402
403 SECURITY CONSIDERATION:
404 This hash only represents the contents of files in the hook directory, not
405 any other files imported or called by hooks. Changes to imported files
406 can change the script behavior without affecting the hash.
407
408 Returns:
409 A string representing the hash. This will always be ASCII so that it can
410 be printed to the user easily.
411 """
412 assert self._hooks_project, "Must have hooks to calculate their hash."
413
414 # We will use the work_git object rather than just calling GetRevisionId().
415 # That gives us a hash of the latest checked in version of the files that
416 # the user will actually be executing. Specifically, GetRevisionId()
417 # doesn't appear to change even if a user checks out a different version
418 # of the hooks repo (via git checkout) nor if a user commits their own revs.
419 #
420 # NOTE: Local (non-committed) changes will not be factored into this hash.
421 # I think this is OK, since we're really only worried about warning the user
422 # about upstream changes.
423 return self._hooks_project.work_git.rev_parse('HEAD')
424
425 def _GetMustVerb(self):
426 """Return 'must' if the hook is required; 'should' if not."""
427 if self._abort_if_user_denies:
428 return 'must'
429 else:
430 return 'should'
431
432 def _CheckForHookApproval(self):
433 """Check to see whether this hook has been approved.
434
Mike Frysinger40252c22016-08-15 21:23:44 -0400435 We'll accept approval of manifest URLs if they're using secure transports.
436 This way the user can say they trust the manifest hoster. For insecure
437 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800438
439 Note that we ask permission for each individual hook even though we use
440 the hash of all hooks when detecting changes. We'd like the user to be
441 able to approve / deny each hook individually. We only use the hash of all
442 hooks because there is no other easy way to detect changes to local imports.
443
444 Returns:
445 True if this hook is approved to run; False otherwise.
446
447 Raises:
448 HookError: Raised if the user doesn't approve and abort_if_user_denies
449 was passed to the consturctor.
450 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400451 if self._ManifestUrlHasSecureScheme():
452 return self._CheckForHookApprovalManifest()
453 else:
454 return self._CheckForHookApprovalHash()
455
456 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
457 changed_prompt):
458 """Check for approval for a particular attribute and hook.
459
460 Args:
461 subkey: The git config key under [repo.hooks.<hook_type>] to store the
462 last approved string.
463 new_val: The new value to compare against the last approved one.
464 main_prompt: Message to display to the user to ask for approval.
465 changed_prompt: Message explaining why we're re-asking for approval.
466
467 Returns:
468 True if this hook is approved to run; False otherwise.
469
470 Raises:
471 HookError: Raised if the user doesn't approve and abort_if_user_denies
472 was passed to the consturctor.
473 """
Doug Anderson37282b42011-03-04 11:54:18 -0800474 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800476
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 # Get the last value that the user approved for this hook; may be None.
478 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800479
Mike Frysinger40252c22016-08-15 21:23:44 -0400480 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800481 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # Approval matched. We're done.
484 return True
485 else:
486 # Give the user a reason why we're prompting, since they last told
487 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400488 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800489 else:
490 prompt = ''
491
492 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
493 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400494 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530495 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900496 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800497
498 # User is doing a one-time approval.
499 if response in ('y', 'yes'):
500 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400501 elif response == 'always':
502 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800503 return True
504
505 # For anything else, we'll assume no approval.
506 if self._abort_if_user_denies:
507 raise HookError('You must allow the %s hook or use --no-verify.' %
508 self._hook_type)
509
510 return False
511
Mike Frysinger40252c22016-08-15 21:23:44 -0400512 def _ManifestUrlHasSecureScheme(self):
513 """Check if the URI for the manifest is a secure transport."""
514 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
515 parse_results = urllib.parse.urlparse(self._manifest_url)
516 return parse_results.scheme in secure_schemes
517
518 def _CheckForHookApprovalManifest(self):
519 """Check whether the user has approved this manifest host.
520
521 Returns:
522 True if this hook is approved to run; False otherwise.
523 """
524 return self._CheckForHookApprovalHelper(
525 'approvedmanifest',
526 self._manifest_url,
527 'Run hook scripts from %s' % (self._manifest_url,),
528 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
529
530 def _CheckForHookApprovalHash(self):
531 """Check whether the user has approved the hooks repo.
532
533 Returns:
534 True if this hook is approved to run; False otherwise.
535 """
536 prompt = ('Repo %s run the script:\n'
537 ' %s\n'
538 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700539 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400540 return self._CheckForHookApprovalHelper(
541 'approvedhash',
542 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700543 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400544 'Scripts have changed since %s was allowed.' % (self._hook_type,))
545
Doug Anderson37282b42011-03-04 11:54:18 -0800546 def _ExecuteHook(self, **kwargs):
547 """Actually execute the given hook.
548
549 This will run the hook's 'main' function in our python interpreter.
550
551 Args:
552 kwargs: Keyword arguments to pass to the hook. These are often specific
553 to the hook type. For instance, pre-upload hooks will contain
554 a project_list.
555 """
556 # Keep sys.path and CWD stashed away so that we can always restore them
557 # upon function exit.
558 orig_path = os.getcwd()
559 orig_syspath = sys.path
560
561 try:
562 # Always run hooks with CWD as topdir.
563 os.chdir(self._topdir)
564
565 # Put the hook dir as the first item of sys.path so hooks can do
566 # relative imports. We want to replace the repo dir as [0] so
567 # hooks can't import repo files.
568 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
569
570 # Exec, storing global context in the context dict. We catch exceptions
571 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500572 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800573 try:
Anthony King70f68902014-05-05 21:15:34 +0100574 exec(compile(open(self._script_fullpath).read(),
575 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800576 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700577 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
578 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800579
580 # Running the script should have defined a main() function.
581 if 'main' not in context:
582 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
583
Doug Anderson37282b42011-03-04 11:54:18 -0800584 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
585 # We don't actually want hooks to define their main with this argument--
586 # it's there to remind them that their hook should always take **kwargs.
587 # For instance, a pre-upload hook should be defined like:
588 # def main(project_list, **kwargs):
589 #
590 # This allows us to later expand the API without breaking old hooks.
591 kwargs = kwargs.copy()
592 kwargs['hook_should_take_kwargs'] = True
593
594 # Call the main function in the hook. If the hook should cause the
595 # build to fail, it will raise an Exception. We'll catch that convert
596 # to a HookError w/ just the failing traceback.
597 try:
598 context['main'](**kwargs)
599 except Exception:
600 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700601 'above.' % (traceback.format_exc(),
602 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800603 finally:
604 # Restore sys.path and CWD.
605 sys.path = orig_syspath
606 os.chdir(orig_path)
607
608 def Run(self, user_allows_all_hooks, **kwargs):
609 """Run the hook.
610
611 If the hook doesn't exist (because there is no hooks project or because
612 this particular hook is not enabled), this is a no-op.
613
614 Args:
615 user_allows_all_hooks: If True, we will never prompt about running the
616 hook--we'll just assume it's OK to run it.
617 kwargs: Keyword arguments to pass to the hook. These are often specific
618 to the hook type. For instance, pre-upload hooks will contain
619 a project_list.
620
621 Raises:
622 HookError: If there was a problem finding the hook or the user declined
623 to run a required hook (from _CheckForHookApproval).
624 """
625 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700626 if ((not self._hooks_project) or (self._hook_type not in
627 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800628 return
629
630 # Bail with a nice error if we can't find the hook.
631 if not os.path.isfile(self._script_fullpath):
632 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
633
634 # Make sure the user is OK with running the hook.
635 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
636 return
637
638 # Run the hook with the same version of python we're using.
639 self._ExecuteHook(**kwargs)
640
641
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600643 # These objects can be shared between several working trees.
644 shareable_files = ['description', 'info']
645 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
646 # These objects can only be used by a single working tree.
647 working_tree_files = ['config', 'packed-refs', 'shallow']
648 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700649
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650 def __init__(self,
651 manifest,
652 name,
653 remote,
654 gitdir,
David James8d201162013-10-11 17:03:19 -0700655 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 worktree,
657 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700658 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800659 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100660 rebase=True,
661 groups=None,
662 sync_c=False,
663 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900664 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100665 clone_depth=None,
666 upstream=None,
667 parent=None,
668 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900669 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700670 optimized_fetch=False,
671 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800672 """Init a Project object.
673
674 Args:
675 manifest: The XmlManifest object.
676 name: The `name` attribute of manifest.xml's project element.
677 remote: RemoteSpec object specifying its remote's properties.
678 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700679 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800680 worktree: Absolute path of git working tree.
681 relpath: Relative path of git working tree to repo's top directory.
682 revisionExpr: The `revision` attribute of manifest.xml's project element.
683 revisionId: git commit id for checking out.
684 rebase: The `rebase` attribute of manifest.xml's project element.
685 groups: The `groups` attribute of manifest.xml's project element.
686 sync_c: The `sync-c` attribute of manifest.xml's project element.
687 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900688 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800689 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
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900721 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900722 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700723 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 self.parent = parent
725 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900726 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800727 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800728
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700729 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500731 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500732 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700733 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
734 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800736 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700737 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800738 else:
739 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700740 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700741 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700742 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400743 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700744 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745
Doug Anderson37282b42011-03-04 11:54:18 -0800746 # This will be filled in if a project is later identified to be the
747 # project containing repo hooks.
748 self.enabled_repo_hooks = []
749
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700750 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800751 def Derived(self):
752 return self.is_derived
753
754 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700756 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757
758 @property
759 def CurrentBranch(self):
760 """Obtain the name of the currently checked out branch.
761 The branch name omits the 'refs/heads/' prefix.
762 None is returned if the project is on a detached HEAD.
763 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700764 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 if b.startswith(R_HEADS):
766 return b[len(R_HEADS):]
767 return None
768
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700769 def IsRebaseInProgress(self):
770 w = self.worktree
771 g = os.path.join(w, '.git')
772 return os.path.exists(os.path.join(g, 'rebase-apply')) \
773 or os.path.exists(os.path.join(g, 'rebase-merge')) \
774 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200775
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 def IsDirty(self, consider_untracked=True):
777 """Is the working directory modified in some way?
778 """
779 self.work_git.update_index('-q',
780 '--unmerged',
781 '--ignore-missing',
782 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900783 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784 return True
785 if self.work_git.DiffZ('diff-files'):
786 return True
787 if consider_untracked and self.work_git.LsOthers():
788 return True
789 return False
790
791 _userident_name = None
792 _userident_email = None
793
794 @property
795 def UserName(self):
796 """Obtain the user's personal name.
797 """
798 if self._userident_name is None:
799 self._LoadUserIdentity()
800 return self._userident_name
801
802 @property
803 def UserEmail(self):
804 """Obtain the user's email address. This is very likely
805 to be their Gerrit login.
806 """
807 if self._userident_email is None:
808 self._LoadUserIdentity()
809 return self._userident_email
810
811 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900812 u = self.bare_git.var('GIT_COMMITTER_IDENT')
813 m = re.compile("^(.*) <([^>]*)> ").match(u)
814 if m:
815 self._userident_name = m.group(1)
816 self._userident_email = m.group(2)
817 else:
818 self._userident_name = ''
819 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
821 def GetRemote(self, name):
822 """Get the configuration for a single remote.
823 """
824 return self.config.GetRemote(name)
825
826 def GetBranch(self, name):
827 """Get the configuration for a single branch.
828 """
829 return self.config.GetBranch(name)
830
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 def GetBranches(self):
832 """Get all existing local branches.
833 """
834 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900835 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530838 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700839 if name.startswith(R_HEADS):
840 name = name[len(R_HEADS):]
841 b = self.GetBranch(name)
842 b.current = name == current
843 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845 heads[name] = b
846
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530847 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700848 if name.startswith(R_PUB):
849 name = name[len(R_PUB):]
850 b = heads.get(name)
851 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900852 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700853
854 return heads
855
Colin Cross5acde752012-03-28 20:15:45 -0700856 def MatchesGroups(self, manifest_groups):
857 """Returns true if the manifest groups specified at init should cause
858 this project to be synced.
859 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700861
862 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700864 manifest_groups: "-group1,group2"
865 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500866
867 The special manifest group "default" will match any project that
868 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700869 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500870 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700872 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500873 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700874
Conley Owens971de8e2012-04-16 10:36:08 -0700875 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876 for group in expanded_manifest_groups:
877 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700878 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700879 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700880 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700881
Conley Owens971de8e2012-04-16 10:36:08 -0700882 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700884# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 def UncommitedFiles(self, get_all=True):
886 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700888 Args:
889 get_all: a boolean, if True - get information about all different
890 uncommitted files. If False - return as soon as any kind of
891 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500892 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700893 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894 self.work_git.update_index('-q',
895 '--unmerged',
896 '--ignore-missing',
897 '--refresh')
898 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700899 details.append("rebase in progress")
900 if not get_all:
901 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500902
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700903 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
904 if changes:
905 details.extend(changes)
906 if not get_all:
907 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500908
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700909 changes = self.work_git.DiffZ('diff-files').keys()
910 if changes:
911 details.extend(changes)
912 if not get_all:
913 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500914
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700915 changes = self.work_git.LsOthers()
916 if changes:
917 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500918
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700919 return details
920
921 def HasChanges(self):
922 """Returns true if there are uncommitted changes.
923 """
924 if self.UncommitedFiles(get_all=False):
925 return True
926 else:
927 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500928
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600929 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200931
932 Args:
933 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600934 quiet: If True then only print the project name. Do not print
935 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700937 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700938 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200939 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700940 print(file=output_redir)
941 print('project %s/' % self.relpath, file=output_redir)
942 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700943 return
944
945 self.work_git.update_index('-q',
946 '--unmerged',
947 '--ignore-missing',
948 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700949 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
951 df = self.work_git.DiffZ('diff-files')
952 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100953 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700954 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
956 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700957 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200958 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700959 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700960
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600961 if quiet:
962 out.nl()
963 return 'DIRTY'
964
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700965 branch = self.CurrentBranch
966 if branch is None:
967 out.nobranch('(*** NO BRANCH ***)')
968 else:
969 out.branch('branch %s', branch)
970 out.nl()
971
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700972 if rb:
973 out.important('prior sync failed; rebase still in progress')
974 out.nl()
975
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976 paths = list()
977 paths.extend(di.keys())
978 paths.extend(df.keys())
979 paths.extend(do)
980
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530981 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900982 try:
983 i = di[p]
984 except KeyError:
985 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900987 try:
988 f = df[p]
989 except KeyError:
990 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200991
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900992 if i:
993 i_status = i.status.upper()
994 else:
995 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700996
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900997 if f:
998 f_status = f.status.lower()
999 else:
1000 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
1002 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001003 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001004 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005 else:
1006 line = ' %s%s\t%s' % (i_status, f_status, p)
1007
1008 if i and not f:
1009 out.added('%s', line)
1010 elif (i and f) or (not i and f):
1011 out.changed('%s', line)
1012 elif not i and not f:
1013 out.untracked('%s', line)
1014 else:
1015 out.write('%s', line)
1016 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001017
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001018 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001019
pelyad67872d2012-03-28 14:49:58 +03001020 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 """Prints the status of the repository to stdout.
1022 """
1023 out = DiffColoring(self.config)
1024 cmd = ['diff']
1025 if out.is_on:
1026 cmd.append('--color')
1027 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001028 if absolute_paths:
1029 cmd.append('--src-prefix=a/%s/' % self.relpath)
1030 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 cmd.append('--')
1032 p = GitCommand(self,
1033 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001034 capture_stdout=True,
1035 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 has_diff = False
1037 for line in p.process.stdout:
1038 if not has_diff:
1039 out.nl()
1040 out.project('project %s/' % self.relpath)
1041 out.nl()
1042 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001043 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044 p.Wait()
1045
1046
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001047# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
David Pursehouse8a68ff92012-09-24 12:15:13 +09001049 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 """Was the branch published (uploaded) for code review?
1051 If so, returns the SHA-1 hash of the last published
1052 state for the branch.
1053 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001054 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001055 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001056 try:
1057 return self.bare_git.rev_parse(key)
1058 except GitError:
1059 return None
1060 else:
1061 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001062 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001063 except KeyError:
1064 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 """Prunes any stale published refs.
1068 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 if all_refs is None:
1070 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 heads = set()
1072 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301073 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 if name.startswith(R_HEADS):
1075 heads.add(name)
1076 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001077 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301079 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080 n = name[len(R_PUB):]
1081 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001082 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001084 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 """List any branches which can be uploaded for review.
1086 """
1087 heads = {}
1088 pubed = {}
1089
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301090 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001092 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001094 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095
1096 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301097 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001098 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001099 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001100 if selected_branch and branch != selected_branch:
1101 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001103 rb = self.GetUploadableBranch(branch)
1104 if rb:
1105 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001106 return ready
1107
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001108 def GetUploadableBranch(self, branch_name):
1109 """Get a single uploadable branch, or None.
1110 """
1111 branch = self.GetBranch(branch_name)
1112 base = branch.LocalMerge
1113 if branch.LocalMerge:
1114 rb = ReviewableBranch(self, branch, base)
1115 if rb.commits:
1116 return rb
1117 return None
1118
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001119 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001120 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001121 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001122 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001123 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001124 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001125 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001126 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001127 validate_certs=True,
1128 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001129 """Uploads the named branch for code review.
1130 """
1131 if branch is None:
1132 branch = self.CurrentBranch
1133 if branch is None:
1134 raise GitError('not currently on a branch')
1135
1136 branch = self.GetBranch(branch)
1137 if not branch.LocalMerge:
1138 raise GitError('branch %s does not track a remote' % branch.name)
1139 if not branch.remote.review:
1140 raise GitError('remote %s has no review url' % branch.remote.name)
1141
Bryan Jacobsf609f912013-05-06 13:36:24 -04001142 if dest_branch is None:
1143 dest_branch = self.dest_branch
1144 if dest_branch is None:
1145 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001146 if not dest_branch.startswith(R_HEADS):
1147 dest_branch = R_HEADS + dest_branch
1148
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001149 if not branch.remote.projectname:
1150 branch.remote.projectname = self.name
1151 branch.remote.Save()
1152
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001153 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001154 if url is None:
1155 raise UploadError('review not configured')
1156 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001157
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001158 if url.startswith('ssh://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001159 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001160
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001161 for push_option in (push_options or []):
1162 cmd.append('-o')
1163 cmd.append(push_option)
1164
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001165 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001166
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 if dest_branch.startswith(R_HEADS):
1168 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001169
1170 upload_type = 'for'
1171 if draft:
1172 upload_type = 'drafts'
1173
1174 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1175 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001176 if auto_topic:
1177 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001178
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001179 opts = ['r=%s' % p for p in people[0]]
1180 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001181 if notify:
1182 opts += ['notify=' + notify]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001183 if private:
1184 opts += ['private']
1185 if wip:
1186 opts += ['wip']
1187 if opts:
1188 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001189 cmd.append(ref_spec)
1190
Anthony King7bdac712014-07-16 12:56:40 +01001191 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001192 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001193
1194 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1195 self.bare_git.UpdateRef(R_PUB + branch.name,
1196 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001197 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001198
1199
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001200# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
Julien Campergue335f5ef2013-10-16 11:02:35 +02001202 def _ExtractArchive(self, tarpath, path=None):
1203 """Extract the given tar on its current location
1204
1205 Args:
1206 - tarpath: The path to the actual tar file
1207
1208 """
1209 try:
1210 with tarfile.open(tarpath, 'r') as tar:
1211 tar.extractall(path=path)
1212 return True
1213 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001214 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001215 return False
1216
Ningning Xiac2fbc782016-08-22 14:24:39 -07001217 def CachePopulate(self, cache_dir, url):
1218 """Populate cache in the cache_dir.
1219
1220 Args:
1221 cache_dir: Directory to cache git files from Google Storage.
1222 url: Git url of current repository.
1223
1224 Raises:
1225 CacheApplyError if it fails to populate the git cache.
1226 """
1227 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1228 '--cache-dir', cache_dir, url]
1229
1230 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1231 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1232 'url: %s' % (cache_dir, url))
1233
1234 def CacheExists(self, cache_dir, url):
1235 """Check the existence of the cache files.
1236
1237 Args:
1238 cache_dir: Directory to cache git files.
1239 url: Git url of current repository.
1240
1241 Raises:
1242 CacheApplyError if the cache files do not exist.
1243 """
1244 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1245
1246 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1247 if exist.Wait() != 0:
1248 raise CacheApplyError('Failed to execute git cache exists cmd. '
1249 'cache_dir: %s url: %s' % (cache_dir, url))
1250
1251 if not exist.stdout or not exist.stdout.strip():
1252 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1253 'url: %s' % (cache_dir, url))
1254 return exist.stdout.strip()
1255
1256 def CacheApply(self, cache_dir):
1257 """Apply git cache files populated from Google Storage buckets.
1258
1259 Args:
1260 cache_dir: Directory to cache git files.
1261
1262 Raises:
1263 CacheApplyError if it fails to apply git caches.
1264 """
1265 remote = self.GetRemote(self.remote.name)
1266
1267 self.CachePopulate(cache_dir, remote.url)
1268
1269 mirror_dir = self.CacheExists(cache_dir, remote.url)
1270
1271 refspec = RefSpec(True, 'refs/heads/*',
1272 'refs/remotes/%s/*' % remote.name)
1273
1274 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1275 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1276 raise CacheApplyError('Failed to fetch refs %s from %s' %
1277 (mirror_dir, str(refspec)))
1278
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001279 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001280 quiet=False,
1281 is_new=None,
1282 current_branch_only=False,
1283 force_sync=False,
1284 clone_bundle=True,
1285 no_tags=False,
1286 archive=False,
1287 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001288 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001289 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001290 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291 """Perform only the network IO portion of the sync process.
1292 Local working directory/branch state is not affected.
1293 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001294 if archive and not isinstance(self, MetaProject):
1295 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001296 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001297 return False
1298
1299 name = self.relpath.replace('\\', '/')
1300 name = name.replace('/', '_')
1301 tarpath = '%s.tar' % name
1302 topdir = self.manifest.topdir
1303
1304 try:
1305 self._FetchArchive(tarpath, cwd=topdir)
1306 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001307 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001308 return False
1309
1310 # From now on, we only need absolute tarpath
1311 tarpath = os.path.join(topdir, tarpath)
1312
1313 if not self._ExtractArchive(tarpath, path=topdir):
1314 return False
1315 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001316 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001317 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001318 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001319 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001320 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001321 if is_new is None:
1322 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001323 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001324 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001325 else:
1326 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001327 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001328
1329 if is_new:
1330 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1331 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001332 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001333 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001334 # This works for both absolute and relative alternate directories.
1335 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001336 finally:
1337 fd.close()
1338 except IOError:
1339 alt_dir = None
1340 else:
1341 alt_dir = None
1342
Ningning Xiac2fbc782016-08-22 14:24:39 -07001343 applied_cache = False
1344 # If cache_dir is provided, and it's a new repository without
1345 # alternative_dir, bootstrap this project repo with the git
1346 # cache files.
1347 if cache_dir is not None and is_new and alt_dir is None:
1348 try:
1349 self.CacheApply(cache_dir)
1350 applied_cache = True
1351 is_new = False
1352 except CacheApplyError as e:
1353 _error('Could not apply git cache: %s', e)
1354 _error('Please check if you have the right GS credentials.')
1355 _error('Please check if the cache files exist in GS.')
1356
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001357 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001358 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001359 and alt_dir is None \
1360 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001361 is_new = False
1362
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001363 if not current_branch_only:
1364 if self.sync_c:
1365 current_branch_only = True
1366 elif not self.manifest._loaded:
1367 # Manifest cannot check defaults until it syncs.
1368 current_branch_only = False
1369 elif self.manifest.default.sync_c:
1370 current_branch_only = True
1371
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001372 if not no_tags:
1373 if not self.sync_tags:
1374 no_tags = True
1375
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001376 if self.clone_depth:
1377 depth = self.clone_depth
1378 else:
1379 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1380
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001381 need_to_fetch = not (optimized_fetch and
1382 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001383 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001384 if (need_to_fetch and
1385 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1386 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001387 no_tags=no_tags, prune=prune, depth=depth,
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04001388 submodules=submodules, force_sync=force_sync)):
Anthony King7bdac712014-07-16 12:56:40 +01001389 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001390
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001391 mp = self.manifest.manifestProject
1392 dissociate = mp.config.GetBoolean('repo.dissociate')
1393 if dissociate:
1394 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1395 if os.path.exists(alternates_file):
1396 cmd = ['repack', '-a', '-d']
1397 if GitCommand(self, cmd, bare=True).Wait() != 0:
1398 return False
1399 platform_utils.remove(alternates_file)
1400
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001401 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001402 self._InitMRef()
1403 else:
1404 self._InitMirrorHead()
1405 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001406 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001407 except OSError:
1408 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001409 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001410
1411 def PostRepoUpgrade(self):
1412 self._InitHooks()
1413
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001414 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001415 if self.manifest.isGitcClient:
1416 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001417 for copyfile in self.copyfiles:
1418 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001419 for linkfile in self.linkfiles:
1420 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001421
Julien Camperguedd654222014-01-09 16:21:37 +01001422 def GetCommitRevisionId(self):
1423 """Get revisionId of a commit.
1424
1425 Use this method instead of GetRevisionId to get the id of the commit rather
1426 than the id of the current git object (for example, a tag)
1427
1428 """
1429 if not self.revisionExpr.startswith(R_TAGS):
1430 return self.GetRevisionId(self._allrefs)
1431
1432 try:
1433 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
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))
Julien Camperguedd654222014-01-09 16:21:37 +01001437
David Pursehouse8a68ff92012-09-24 12:15:13 +09001438 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001439 if self.revisionId:
1440 return self.revisionId
1441
1442 rem = self.GetRemote(self.remote.name)
1443 rev = rem.ToLocal(self.revisionExpr)
1444
David Pursehouse8a68ff92012-09-24 12:15:13 +09001445 if all_refs is not None and rev in all_refs:
1446 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001447
1448 try:
1449 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1450 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001451 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1452 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001453
Martin Kellye4e94d22017-03-21 16:05:12 -07001454 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 """Perform only the local IO portion of the sync process.
1456 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001457 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001458 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001459 all_refs = self.bare_ref.all
1460 self.CleanPublishedCache(all_refs)
1461 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001462
David Pursehouse1d947b32012-10-25 12:23:11 +09001463 def _doff():
1464 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001465 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001466
Martin Kellye4e94d22017-03-21 16:05:12 -07001467 def _dosubmodules():
1468 self._SyncSubmodules(quiet=True)
1469
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001470 head = self.work_git.GetHead()
1471 if head.startswith(R_HEADS):
1472 branch = head[len(R_HEADS):]
1473 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001474 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001475 except KeyError:
1476 head = None
1477 else:
1478 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001479
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001480 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001481 # Currently on a detached HEAD. The user is assumed to
1482 # not have any local modifications worth worrying about.
1483 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001484 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001485 syncbuf.fail(self, _PriorSyncFailedError())
1486 return
1487
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001488 if head == revid:
1489 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001490 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001491 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001492 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001493 # The copy/linkfile config may have changed.
1494 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001495 return
1496 else:
1497 lost = self._revlist(not_rev(revid), HEAD)
1498 if lost:
1499 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001500
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001501 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001502 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001503 if submodules:
1504 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001505 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001506 syncbuf.fail(self, e)
1507 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001508 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001509 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001510
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001511 if head == revid:
1512 # No changes; don't do anything further.
1513 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001514 # The copy/linkfile config may have changed.
1515 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001516 return
1517
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001518 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001520 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001521 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001522 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001524 syncbuf.info(self,
1525 "leaving %s; does not track upstream",
1526 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001527 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001528 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001529 if submodules:
1530 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001531 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001532 syncbuf.fail(self, e)
1533 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001534 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001535 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001536
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001537 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001538 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001539 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001540 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541 if not_merged:
1542 if upstream_gain:
1543 # The user has published this branch and some of those
1544 # commits are not yet merged upstream. We do not want
1545 # to rewrite the published commits so we punt.
1546 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001547 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001548 "branch %s is published (but not merged) and is now "
1549 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001550 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001551 elif pub == head:
1552 # All published commits are merged, and thus we are a
1553 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001554 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001555 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001556 if submodules:
1557 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001558 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001560 # Examine the local commits not in the remote. Find the
1561 # last one attributed to this user, if any.
1562 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001563 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001564 last_mine = None
1565 cnt_mine = 0
1566 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301567 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001568 if committer_email == self.UserEmail:
1569 last_mine = commit_id
1570 cnt_mine += 1
1571
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001572 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001573 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001574
1575 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001576 syncbuf.fail(self, _DirtyError())
1577 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001578
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001579 # If the upstream switched on us, warn the user.
1580 #
1581 if branch.merge != self.revisionExpr:
1582 if branch.merge and self.revisionExpr:
1583 syncbuf.info(self,
1584 'manifest switched %s...%s',
1585 branch.merge,
1586 self.revisionExpr)
1587 elif branch.merge:
1588 syncbuf.info(self,
1589 'manifest no longer tracks %s',
1590 branch.merge)
1591
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001592 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001593 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001594 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001595 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001596 syncbuf.info(self,
1597 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001598 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001599
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001600 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001601 if not ID_RE.match(self.revisionExpr):
1602 # in case of manifest sync the revisionExpr might be a SHA1
1603 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001604 if not branch.merge.startswith('refs/'):
1605 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001606 branch.Save()
1607
Mike Pontillod3153822012-02-28 11:53:24 -08001608 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001609 def _docopyandlink():
1610 self._CopyAndLinkFiles()
1611
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001612 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001613 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001614 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001615 if submodules:
1616 syncbuf.later2(self, _dosubmodules)
1617 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001618 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001619 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001620 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001621 if submodules:
1622 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001623 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001624 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001625 syncbuf.fail(self, e)
1626 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001628 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001629 if submodules:
1630 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001631
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001632 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001633 # dest should already be an absolute path, but src is project relative
1634 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001635 abssrc = os.path.join(self.worktree, src)
1636 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001637
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001638 def AddLinkFile(self, src, dest, absdest):
1639 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001640 # make src relative path to dest
1641 absdestdir = os.path.dirname(absdest)
1642 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001643 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001644
James W. Mills24c13082012-04-12 15:04:13 -05001645 def AddAnnotation(self, name, value, keep):
1646 self.annotations.append(_Annotation(name, value, keep))
1647
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001648 def DownloadPatchSet(self, change_id, patch_id):
1649 """Download a single patch set of a single change to FETCH_HEAD.
1650 """
1651 remote = self.GetRemote(self.remote.name)
1652
1653 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001654 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001655 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001656 if GitCommand(self, cmd, bare=True).Wait() != 0:
1657 return None
1658 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001659 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001660 change_id,
1661 patch_id,
1662 self.bare_git.rev_parse('FETCH_HEAD'))
1663
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001664
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001665# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001666
Simran Basib9a1b732015-08-20 12:19:28 -07001667 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001668 """Create a new branch off the manifest's revision.
1669 """
Simran Basib9a1b732015-08-20 12:19:28 -07001670 if not branch_merge:
1671 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001672 head = self.work_git.GetHead()
1673 if head == (R_HEADS + name):
1674 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001675
David Pursehouse8a68ff92012-09-24 12:15:13 +09001676 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001677 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001678 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001679 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001680 capture_stdout=True,
1681 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001682
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001683 branch = self.GetBranch(name)
1684 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001685 branch.merge = branch_merge
1686 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1687 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001688 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001689
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001690 if head.startswith(R_HEADS):
1691 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001692 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001693 except KeyError:
1694 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001695 if revid and head and revid == head:
1696 ref = os.path.join(self.gitdir, R_HEADS + name)
1697 try:
1698 os.makedirs(os.path.dirname(ref))
1699 except OSError:
1700 pass
1701 _lwrite(ref, '%s\n' % revid)
1702 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1703 'ref: %s%s\n' % (R_HEADS, name))
1704 branch.Save()
1705 return True
1706
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001707 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001708 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001709 capture_stdout=True,
1710 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001711 branch.Save()
1712 return True
1713 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714
Wink Saville02d79452009-04-10 13:01:24 -07001715 def CheckoutBranch(self, name):
1716 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001717
1718 Args:
1719 name: The name of the branch to checkout.
1720
1721 Returns:
1722 True if the checkout succeeded; False if it didn't; None if the branch
1723 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001724 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001725 rev = R_HEADS + name
1726 head = self.work_git.GetHead()
1727 if head == rev:
1728 # Already on the branch
1729 #
1730 return True
Wink Saville02d79452009-04-10 13:01:24 -07001731
David Pursehouse8a68ff92012-09-24 12:15:13 +09001732 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001733 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001734 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001735 except KeyError:
1736 # Branch does not exist in this project
1737 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001738 return None
Wink Saville02d79452009-04-10 13:01:24 -07001739
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001740 if head.startswith(R_HEADS):
1741 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001742 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001743 except KeyError:
1744 head = None
1745
1746 if head == revid:
1747 # Same revision; just update HEAD to point to the new
1748 # target branch, but otherwise take no other action.
1749 #
1750 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1751 'ref: %s%s\n' % (R_HEADS, name))
1752 return True
1753
1754 return GitCommand(self,
1755 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001756 capture_stdout=True,
1757 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001758
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001759 def AbandonBranch(self, name):
1760 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001761
1762 Args:
1763 name: The name of the branch to abandon.
1764
1765 Returns:
1766 True if the abandon succeeded; False if it didn't; None if the branch
1767 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001768 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001769 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001770 all_refs = self.bare_ref.all
1771 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001772 # Doesn't exist
1773 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001774
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001775 head = self.work_git.GetHead()
1776 if head == rev:
1777 # We can't destroy the branch while we are sitting
1778 # on it. Switch to a detached HEAD.
1779 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001780 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001781
David Pursehouse8a68ff92012-09-24 12:15:13 +09001782 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001783 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001784 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1785 '%s\n' % revid)
1786 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001787 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001788
1789 return GitCommand(self,
1790 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001791 capture_stdout=True,
1792 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001793
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001794 def PruneHeads(self):
1795 """Prune any topic branches already merged into upstream.
1796 """
1797 cb = self.CurrentBranch
1798 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001799 left = self._allrefs
1800 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001801 if name.startswith(R_HEADS):
1802 name = name[len(R_HEADS):]
1803 if cb is None or name != cb:
1804 kill.append(name)
1805
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001806 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001807 if cb is not None \
1808 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001809 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001810 self.work_git.DetachHead(HEAD)
1811 kill.append(cb)
1812
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001813 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001814 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001815
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001816 try:
1817 self.bare_git.DetachHead(rev)
1818
1819 b = ['branch', '-d']
1820 b.extend(kill)
1821 b = GitCommand(self, b, bare=True,
1822 capture_stdout=True,
1823 capture_stderr=True)
1824 b.Wait()
1825 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001826 if ID_RE.match(old):
1827 self.bare_git.DetachHead(old)
1828 else:
1829 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001830 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001831
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001832 for branch in kill:
1833 if (R_HEADS + branch) not in left:
1834 self.CleanPublishedCache()
1835 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001836
1837 if cb and cb not in kill:
1838 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001839 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001840
1841 kept = []
1842 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001843 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001844 branch = self.GetBranch(branch)
1845 base = branch.LocalMerge
1846 if not base:
1847 base = rev
1848 kept.append(ReviewableBranch(self, branch, base))
1849 return kept
1850
1851
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001852# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001853
1854 def GetRegisteredSubprojects(self):
1855 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001856
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001857 def rec(subprojects):
1858 if not subprojects:
1859 return
1860 result.extend(subprojects)
1861 for p in subprojects:
1862 rec(p.subprojects)
1863 rec(self.subprojects)
1864 return result
1865
1866 def _GetSubmodules(self):
1867 # Unfortunately we cannot call `git submodule status --recursive` here
1868 # because the working tree might not exist yet, and it cannot be used
1869 # without a working tree in its current implementation.
1870
1871 def get_submodules(gitdir, rev):
1872 # Parse .gitmodules for submodule sub_paths and sub_urls
1873 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1874 if not sub_paths:
1875 return []
1876 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1877 # revision of submodule repository
1878 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1879 submodules = []
1880 for sub_path, sub_url in zip(sub_paths, sub_urls):
1881 try:
1882 sub_rev = sub_revs[sub_path]
1883 except KeyError:
1884 # Ignore non-exist submodules
1885 continue
1886 submodules.append((sub_rev, sub_path, sub_url))
1887 return submodules
1888
1889 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1890 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001891
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001892 def parse_gitmodules(gitdir, rev):
1893 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1894 try:
Anthony King7bdac712014-07-16 12:56:40 +01001895 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1896 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001897 except GitError:
1898 return [], []
1899 if p.Wait() != 0:
1900 return [], []
1901
1902 gitmodules_lines = []
1903 fd, temp_gitmodules_path = tempfile.mkstemp()
1904 try:
1905 os.write(fd, p.stdout)
1906 os.close(fd)
1907 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001908 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1909 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001910 if p.Wait() != 0:
1911 return [], []
1912 gitmodules_lines = p.stdout.split('\n')
1913 except GitError:
1914 return [], []
1915 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001916 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001917
1918 names = set()
1919 paths = {}
1920 urls = {}
1921 for line in gitmodules_lines:
1922 if not line:
1923 continue
1924 m = re_path.match(line)
1925 if m:
1926 names.add(m.group(1))
1927 paths[m.group(1)] = m.group(2)
1928 continue
1929 m = re_url.match(line)
1930 if m:
1931 names.add(m.group(1))
1932 urls[m.group(1)] = m.group(2)
1933 continue
1934 names = sorted(names)
1935 return ([paths.get(name, '') for name in names],
1936 [urls.get(name, '') for name in names])
1937
1938 def git_ls_tree(gitdir, rev, paths):
1939 cmd = ['ls-tree', rev, '--']
1940 cmd.extend(paths)
1941 try:
Anthony King7bdac712014-07-16 12:56:40 +01001942 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1943 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001944 except GitError:
1945 return []
1946 if p.Wait() != 0:
1947 return []
1948 objects = {}
1949 for line in p.stdout.split('\n'):
1950 if not line.strip():
1951 continue
1952 object_rev, object_path = line.split()[2:4]
1953 objects[object_path] = object_rev
1954 return objects
1955
1956 try:
1957 rev = self.GetRevisionId()
1958 except GitError:
1959 return []
1960 return get_submodules(self.gitdir, rev)
1961
1962 def GetDerivedSubprojects(self):
1963 result = []
1964 if not self.Exists:
1965 # If git repo does not exist yet, querying its submodules will
1966 # mess up its states; so return here.
1967 return result
1968 for rev, path, url in self._GetSubmodules():
1969 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001970 relpath, worktree, gitdir, objdir = \
1971 self.manifest.GetSubprojectPaths(self, name, path)
1972 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001973 if project:
1974 result.extend(project.GetDerivedSubprojects())
1975 continue
David James8d201162013-10-11 17:03:19 -07001976
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001977 if url.startswith('..'):
1978 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001979 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001980 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001981 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001982 review=self.remote.review,
1983 revision=self.remote.revision)
1984 subproject = Project(manifest=self.manifest,
1985 name=name,
1986 remote=remote,
1987 gitdir=gitdir,
1988 objdir=objdir,
1989 worktree=worktree,
1990 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001991 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001992 revisionId=rev,
1993 rebase=self.rebase,
1994 groups=self.groups,
1995 sync_c=self.sync_c,
1996 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001997 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001998 parent=self,
1999 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002000 result.append(subproject)
2001 result.extend(subproject.GetDerivedSubprojects())
2002 return result
2003
2004
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002005# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002006 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002007 try:
2008 # if revision (sha or tag) is not present then following function
2009 # throws an error.
2010 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2011 return True
2012 except GitError:
2013 # There is no such persistent revision. We have to fetch it.
2014 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002015
Julien Campergue335f5ef2013-10-16 11:02:35 +02002016 def _FetchArchive(self, tarpath, cwd=None):
2017 cmd = ['archive', '-v', '-o', tarpath]
2018 cmd.append('--remote=%s' % self.remote.url)
2019 cmd.append('--prefix=%s/' % self.relpath)
2020 cmd.append(self.revisionExpr)
2021
2022 command = GitCommand(self, cmd, cwd=cwd,
2023 capture_stdout=True,
2024 capture_stderr=True)
2025
2026 if command.Wait() != 0:
2027 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2028
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002029 def _RemoteFetch(self, name=None,
2030 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002031 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002032 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002033 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002034 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002035 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002036 depth=None,
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002037 submodules=False,
2038 force_sync=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002039
2040 is_sha1 = False
2041 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002042 # The depth should not be used when fetching to a mirror because
2043 # it will result in a shallow repository that cannot be cloned or
2044 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002045 # The repo project should also never be synced with partial depth.
2046 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2047 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002048
Shawn Pearce69e04d82014-01-29 12:48:54 -08002049 if depth:
2050 current_branch_only = True
2051
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002052 if ID_RE.match(self.revisionExpr) is not None:
2053 is_sha1 = True
2054
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002055 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002056 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002057 # this is a tag and its sha1 value should never change
2058 tag_name = self.revisionExpr[len(R_TAGS):]
2059
2060 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002061 if self._CheckForImmutableRevision():
2062 print('Skipped fetching project %s (already have persistent ref)'
2063 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002064 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002065 if is_sha1 and not depth:
2066 # When syncing a specific commit and --depth is not set:
2067 # * if upstream is explicitly specified and is not a sha1, fetch only
2068 # upstream as users expect only upstream to be fetch.
2069 # Note: The commit might not be in upstream in which case the sync
2070 # will fail.
2071 # * otherwise, fetch all branches to make sure we end up with the
2072 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002073 if self.upstream:
2074 current_branch_only = not ID_RE.match(self.upstream)
2075 else:
2076 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002077
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002078 if not name:
2079 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002080
2081 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002082 remote = self.GetRemote(name)
2083 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002084 ssh_proxy = True
2085
Shawn O. Pearce88443382010-10-08 10:02:09 +02002086 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002087 if alt_dir and 'objects' == os.path.basename(alt_dir):
2088 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002089 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2090 remote = self.GetRemote(name)
2091
David Pursehouse8a68ff92012-09-24 12:15:13 +09002092 all_refs = self.bare_ref.all
2093 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002094 tmp = set()
2095
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302096 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002097 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002098 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002099 all_refs[r] = ref_id
2100 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002101 continue
2102
David Pursehouse8a68ff92012-09-24 12:15:13 +09002103 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002104 continue
2105
David Pursehouse8a68ff92012-09-24 12:15:13 +09002106 r = 'refs/_alt/%s' % ref_id
2107 all_refs[r] = ref_id
2108 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002109 tmp.add(r)
2110
heping3d7bbc92017-04-12 19:51:47 +08002111 tmp_packed_lines = []
2112 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002113
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302114 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002115 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002116 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002117 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002118 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002119
heping3d7bbc92017-04-12 19:51:47 +08002120 tmp_packed = ''.join(tmp_packed_lines)
2121 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002122 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002123 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002124 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002126 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002127
Conley Owensf97e8382015-01-21 11:12:46 -08002128 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002129 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002130 else:
2131 # If this repo has shallow objects, then we don't know which refs have
2132 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2133 # do this with projects that don't have shallow objects, since it is less
2134 # efficient.
2135 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2136 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002137
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002138 if quiet:
2139 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002140 if not self.worktree:
2141 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002142 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002143
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002144 # If using depth then we should not get all the tags since they may
2145 # be outside of the depth.
2146 if no_tags or depth:
2147 cmd.append('--no-tags')
2148 else:
2149 cmd.append('--tags')
2150
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002151 if force_sync:
2152 cmd.append('--force')
2153
David Pursehouse74cfd272015-10-14 10:50:15 +09002154 if prune:
2155 cmd.append('--prune')
2156
Martin Kellye4e94d22017-03-21 16:05:12 -07002157 if submodules:
2158 cmd.append('--recurse-submodules=on-demand')
2159
Conley Owens80b87fe2014-05-09 17:13:44 -07002160 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002161 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002162 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002163 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002164 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002165 spec.append('tag')
2166 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002167
David Pursehouse403b64e2015-04-27 10:41:33 +09002168 if not self.manifest.IsMirror:
2169 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002170 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002171 # Shallow checkout of a specific commit, fetch from that commit and not
2172 # the heads only as the commit might be deeper in the history.
2173 spec.append(branch)
2174 else:
2175 if is_sha1:
2176 branch = self.upstream
2177 if branch is not None and branch.strip():
2178 if not branch.startswith('refs/'):
2179 branch = R_HEADS + branch
2180 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002181 cmd.extend(spec)
2182
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002183 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002184 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002185 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002186 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002187 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002188 ok = True
2189 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002190 # If needed, run the 'git remote prune' the first time through the loop
2191 elif (not _i and
2192 "error:" in gitcmd.stderr and
2193 "git remote prune" in gitcmd.stderr):
2194 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002195 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002196 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002197 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002198 break
2199 continue
Brian Harring14a66742012-09-28 20:21:57 -07002200 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002201 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2202 # in sha1 mode, we just tried sync'ing from the upstream field; it
2203 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002204 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002205 elif ret < 0:
2206 # Git died with a signal, exit immediately
2207 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002208 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002209
2210 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002211 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002212 if old_packed != '':
2213 _lwrite(packed_refs, old_packed)
2214 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002215 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002216 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002217
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002218 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002219 # We just synced the upstream given branch; verify we
2220 # got what we wanted, else trigger a second run of all
2221 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002222 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002223 if current_branch_only and depth:
2224 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002225 return self._RemoteFetch(name=name,
2226 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002227 initial=False, quiet=quiet, alt_dir=alt_dir,
2228 depth=None)
2229 else:
2230 # Avoid infinite recursion: sync all branches with depth set to None
2231 return self._RemoteFetch(name=name, current_branch_only=False,
2232 initial=False, quiet=quiet, alt_dir=alt_dir,
2233 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002234
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002235 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002236
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002237 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002238 if initial and \
2239 (self.manifest.manifestProject.config.GetString('repo.depth') or
2240 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002241 return False
2242
2243 remote = self.GetRemote(self.remote.name)
2244 bundle_url = remote.url + '/clone.bundle'
2245 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002246 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2247 'persistent-http',
2248 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002249 return False
2250
2251 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2252 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2253
2254 exist_dst = os.path.exists(bundle_dst)
2255 exist_tmp = os.path.exists(bundle_tmp)
2256
2257 if not initial and not exist_dst and not exist_tmp:
2258 return False
2259
2260 if not exist_dst:
2261 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2262 if not exist_dst:
2263 return False
2264
2265 cmd = ['fetch']
2266 if quiet:
2267 cmd.append('--quiet')
2268 if not self.worktree:
2269 cmd.append('--update-head-ok')
2270 cmd.append(bundle_dst)
2271 for f in remote.fetch:
2272 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002273 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002274
2275 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002276 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002277 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002278 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002279 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002280 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002281
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002282 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002283 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002284 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002285
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002286 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002287 if quiet:
2288 cmd += ['--silent']
2289 if os.path.exists(tmpPath):
2290 size = os.stat(tmpPath).st_size
2291 if size >= 1024:
2292 cmd += ['--continue-at', '%d' % (size,)]
2293 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002294 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002295 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2296 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002297 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002298 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002299 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002300 if srcUrl.startswith('persistent-'):
2301 srcUrl = srcUrl[len('persistent-'):]
2302 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002303
Dave Borowitz137d0132015-01-02 11:12:54 -08002304 if IsTrace():
2305 Trace('%s', ' '.join(cmd))
2306 try:
2307 proc = subprocess.Popen(cmd)
2308 except OSError:
2309 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002310
Dave Borowitz137d0132015-01-02 11:12:54 -08002311 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002312
Dave Borowitz137d0132015-01-02 11:12:54 -08002313 if curlret == 22:
2314 # From curl man page:
2315 # 22: HTTP page not retrieved. The requested url was not found or
2316 # returned another error with the HTTP error code being 400 or above.
2317 # This return code only appears if -f, --fail is used.
2318 if not quiet:
2319 print("Server does not provide clone.bundle; ignoring.",
2320 file=sys.stderr)
2321 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002322
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002323 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002324 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002325 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002326 return True
2327 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002328 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002329 return False
2330 else:
2331 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002332
Kris Giesingc8d882a2014-12-23 13:02:32 -08002333 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002334 try:
2335 with open(path) as f:
2336 if f.read(16) == '# v2 git bundle\n':
2337 return True
2338 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002339 if not quiet:
2340 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002341 return False
2342 except OSError:
2343 return False
2344
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002345 def _Checkout(self, rev, quiet=False):
2346 cmd = ['checkout']
2347 if quiet:
2348 cmd.append('-q')
2349 cmd.append(rev)
2350 cmd.append('--')
2351 if GitCommand(self, cmd).Wait() != 0:
2352 if self._allrefs:
2353 raise GitError('%s checkout %s ' % (self.name, rev))
2354
Anthony King7bdac712014-07-16 12:56:40 +01002355 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002356 cmd = ['cherry-pick']
2357 cmd.append(rev)
2358 cmd.append('--')
2359 if GitCommand(self, cmd).Wait() != 0:
2360 if self._allrefs:
2361 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2362
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302363 def _LsRemote(self, refs):
2364 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302365 p = GitCommand(self, cmd, capture_stdout=True)
2366 if p.Wait() == 0:
2367 if hasattr(p.stdout, 'decode'):
2368 return p.stdout.decode('utf-8')
2369 else:
2370 return p.stdout
2371 return None
2372
Anthony King7bdac712014-07-16 12:56:40 +01002373 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002374 cmd = ['revert']
2375 cmd.append('--no-edit')
2376 cmd.append(rev)
2377 cmd.append('--')
2378 if GitCommand(self, cmd).Wait() != 0:
2379 if self._allrefs:
2380 raise GitError('%s revert %s ' % (self.name, rev))
2381
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002382 def _ResetHard(self, rev, quiet=True):
2383 cmd = ['reset', '--hard']
2384 if quiet:
2385 cmd.append('-q')
2386 cmd.append(rev)
2387 if GitCommand(self, cmd).Wait() != 0:
2388 raise GitError('%s reset --hard %s ' % (self.name, rev))
2389
Martin Kellye4e94d22017-03-21 16:05:12 -07002390 def _SyncSubmodules(self, quiet=True):
2391 cmd = ['submodule', 'update', '--init', '--recursive']
2392 if quiet:
2393 cmd.append('-q')
2394 if GitCommand(self, cmd).Wait() != 0:
2395 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2396
Anthony King7bdac712014-07-16 12:56:40 +01002397 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002398 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002399 if onto is not None:
2400 cmd.extend(['--onto', onto])
2401 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002402 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002403 raise GitError('%s rebase %s ' % (self.name, upstream))
2404
Pierre Tardy3d125942012-05-04 12:18:12 +02002405 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002406 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002407 if ffonly:
2408 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002409 if GitCommand(self, cmd).Wait() != 0:
2410 raise GitError('%s merge %s ' % (self.name, head))
2411
Kevin Degiabaa7f32014-11-12 11:27:45 -07002412 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002413 init_git_dir = not os.path.exists(self.gitdir)
2414 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002415 try:
2416 # Initialize the bare repository, which contains all of the objects.
2417 if init_obj_dir:
2418 os.makedirs(self.objdir)
2419 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002420
Kevin Degib1a07b82015-07-27 13:33:43 -06002421 # If we have a separate directory to hold refs, initialize it as well.
2422 if self.objdir != self.gitdir:
2423 if init_git_dir:
2424 os.makedirs(self.gitdir)
2425
2426 if init_obj_dir or init_git_dir:
2427 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2428 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002429 try:
2430 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2431 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002432 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002433 print("Retrying clone after deleting %s" %
2434 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002435 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002436 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2437 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002438 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002439 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002440 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2441 except:
2442 raise e
2443 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002444
Kevin Degi384b3c52014-10-16 16:02:58 -06002445 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002446 mp = self.manifest.manifestProject
2447 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002448
Kevin Degib1a07b82015-07-27 13:33:43 -06002449 if ref_dir or mirror_git:
2450 if not mirror_git:
2451 mirror_git = os.path.join(ref_dir, self.name + '.git')
2452 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2453 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002454
Kevin Degib1a07b82015-07-27 13:33:43 -06002455 if os.path.exists(mirror_git):
2456 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002457
Kevin Degib1a07b82015-07-27 13:33:43 -06002458 elif os.path.exists(repo_git):
2459 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002460
Kevin Degib1a07b82015-07-27 13:33:43 -06002461 else:
2462 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002463
Kevin Degib1a07b82015-07-27 13:33:43 -06002464 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002465 if not os.path.isabs(ref_dir):
2466 # The alternate directory is relative to the object database.
2467 ref_dir = os.path.relpath(ref_dir,
2468 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002469 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2470 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002471
Kevin Degib1a07b82015-07-27 13:33:43 -06002472 self._UpdateHooks()
2473
2474 m = self.manifest.manifestProject.config
2475 for key in ['user.name', 'user.email']:
2476 if m.Has(key, include_defaults=False):
2477 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002478 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002479 if self.manifest.IsMirror:
2480 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002481 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002482 self.config.SetString('core.bare', None)
2483 except Exception:
2484 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002485 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002486 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002487 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002488 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002489
Jimmie Westera0444582012-10-24 13:44:42 +02002490 def _UpdateHooks(self):
2491 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002492 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002493
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002494 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002495 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002496 if not os.path.exists(hooks):
2497 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002498 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002499 name = os.path.basename(stock_hook)
2500
Victor Boivie65e0f352011-04-18 11:23:29 +02002501 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002502 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002503 # Don't install a Gerrit Code Review hook if this
2504 # project does not appear to use it for reviews.
2505 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002506 # Since the manifest project is one of those, but also
2507 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002508 continue
2509
2510 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002511 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002512 continue
2513 if os.path.exists(dst):
2514 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002515 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002516 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002517 _warn("%s: Not replacing locally modified %s hook",
2518 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002519 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002520 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002521 platform_utils.symlink(
2522 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002523 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002524 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002525 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002526 else:
2527 raise
2528
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002529 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002530 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002531 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002532 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002533 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002534 remote.review = self.remote.review
2535 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002536
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002537 if self.worktree:
2538 remote.ResetFetch(mirror=False)
2539 else:
2540 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002541 remote.Save()
2542
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002543 def _InitMRef(self):
2544 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002545 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002546
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002547 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002548 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002549
2550 def _InitAnyMRef(self, ref):
2551 cur = self.bare_ref.symref(ref)
2552
2553 if self.revisionId:
2554 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2555 msg = 'manifest set to %s' % self.revisionId
2556 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002557 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002558 else:
2559 remote = self.GetRemote(self.remote.name)
2560 dst = remote.ToLocal(self.revisionExpr)
2561 if cur != dst:
2562 msg = 'manifest set to %s' % self.revisionExpr
2563 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002564
Kevin Degi384b3c52014-10-16 16:02:58 -06002565 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002566 symlink_files = self.shareable_files[:]
2567 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002568 if share_refs:
2569 symlink_files += self.working_tree_files
2570 symlink_dirs += self.working_tree_dirs
2571 to_symlink = symlink_files + symlink_dirs
2572 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002573 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002574 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002575 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002576 # Fail if the links are pointing to the wrong place
2577 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002578 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002579 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002580 'work tree. If you\'re comfortable with the '
2581 'possibility of losing the work tree\'s git metadata,'
2582 ' use `repo sync --force-sync {0}` to '
2583 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002584
David James8d201162013-10-11 17:03:19 -07002585 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2586 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2587
2588 Args:
2589 gitdir: The bare git repository. Must already be initialized.
2590 dotgit: The repository you would like to initialize.
2591 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2592 Only one work tree can store refs under a given |gitdir|.
2593 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2594 This saves you the effort of initializing |dotgit| yourself.
2595 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002596 symlink_files = self.shareable_files[:]
2597 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002598 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002599 symlink_files += self.working_tree_files
2600 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002601 to_symlink = symlink_files + symlink_dirs
2602
2603 to_copy = []
2604 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002605 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002606
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002607 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002608 for name in set(to_copy).union(to_symlink):
2609 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002610 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002611 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002612
Kevin Degi384b3c52014-10-16 16:02:58 -06002613 if os.path.lexists(dst):
2614 continue
David James8d201162013-10-11 17:03:19 -07002615
2616 # If the source dir doesn't exist, create an empty dir.
2617 if name in symlink_dirs and not os.path.lexists(src):
2618 os.makedirs(src)
2619
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002620 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002621 platform_utils.symlink(
2622 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002623 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002624 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002625 shutil.copytree(src, dst)
2626 elif os.path.isfile(src):
2627 shutil.copy(src, dst)
2628
Conley Owens80b87fe2014-05-09 17:13:44 -07002629 # If the source file doesn't exist, ensure the destination
2630 # file doesn't either.
2631 if name in symlink_files and not os.path.lexists(src):
2632 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002633 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002634 except OSError:
2635 pass
2636
David James8d201162013-10-11 17:03:19 -07002637 except OSError as e:
2638 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002639 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002640 else:
2641 raise
2642
Martin Kellye4e94d22017-03-21 16:05:12 -07002643 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002644 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002645 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002646 try:
2647 if init_dotgit:
2648 os.makedirs(dotgit)
2649 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2650 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002651
Kevin Degiabaa7f32014-11-12 11:27:45 -07002652 try:
2653 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2654 except GitError as e:
2655 if force_sync:
2656 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002657 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002658 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002659 except:
2660 raise e
2661 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002662
Kevin Degib1a07b82015-07-27 13:33:43 -06002663 if init_dotgit:
2664 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002665
Kevin Degib1a07b82015-07-27 13:33:43 -06002666 cmd = ['read-tree', '--reset', '-u']
2667 cmd.append('-v')
2668 cmd.append(HEAD)
2669 if GitCommand(self, cmd).Wait() != 0:
2670 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002671
Martin Kellye4e94d22017-03-21 16:05:12 -07002672 if submodules:
2673 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002674 self._CopyAndLinkFiles()
2675 except Exception:
2676 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002677 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002678 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002679
Renaud Paquay788e9622017-01-27 11:41:12 -08002680 def _get_symlink_error_message(self):
2681 if platform_utils.isWindows():
2682 return ('Unable to create symbolic link. Please re-run the command as '
2683 'Administrator, or see '
2684 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2685 'for other options.')
2686 return 'filesystem must support symlinks'
2687
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002688 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002689 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002690
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002691 def _revlist(self, *args, **kw):
2692 a = []
2693 a.extend(args)
2694 a.append('--')
2695 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002696
2697 @property
2698 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002699 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002700
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002701 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002702 """Get logs between two revisions of this project."""
2703 comp = '..'
2704 if rev1:
2705 revs = [rev1]
2706 if rev2:
2707 revs.extend([comp, rev2])
2708 cmd = ['log', ''.join(revs)]
2709 out = DiffColoring(self.config)
2710 if out.is_on and color:
2711 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002712 if pretty_format is not None:
2713 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002714 if oneline:
2715 cmd.append('--oneline')
2716
2717 try:
2718 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2719 if log.Wait() == 0:
2720 return log.stdout
2721 except GitError:
2722 # worktree may not exist if groups changed for example. In that case,
2723 # try in gitdir instead.
2724 if not os.path.exists(self.worktree):
2725 return self.bare_git.log(*cmd[1:])
2726 else:
2727 raise
2728 return None
2729
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002730 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2731 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002732 """Get the list of logs from this revision to given revisionId"""
2733 logs = {}
2734 selfId = self.GetRevisionId(self._allrefs)
2735 toId = toProject.GetRevisionId(toProject._allrefs)
2736
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002737 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2738 pretty_format=pretty_format)
2739 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2740 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002741 return logs
2742
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002743 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002744
David James8d201162013-10-11 17:03:19 -07002745 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746 self._project = project
2747 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002748 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002749
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002750 def LsOthers(self):
2751 p = GitCommand(self._project,
2752 ['ls-files',
2753 '-z',
2754 '--others',
2755 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002756 bare=False,
David James8d201162013-10-11 17:03:19 -07002757 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002758 capture_stdout=True,
2759 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002760 if p.Wait() == 0:
2761 out = p.stdout
2762 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002763 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002764 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002765 return []
2766
2767 def DiffZ(self, name, *args):
2768 cmd = [name]
2769 cmd.append('-z')
2770 cmd.extend(args)
2771 p = GitCommand(self._project,
2772 cmd,
David James8d201162013-10-11 17:03:19 -07002773 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002774 bare=False,
2775 capture_stdout=True,
2776 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002777 try:
2778 out = p.process.stdout.read()
2779 r = {}
2780 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002781 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002782 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002783 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002784 info = next(out)
2785 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002786 except StopIteration:
2787 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002788
2789 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002790
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002791 def __init__(self, path, omode, nmode, oid, nid, state):
2792 self.path = path
2793 self.src_path = None
2794 self.old_mode = omode
2795 self.new_mode = nmode
2796 self.old_id = oid
2797 self.new_id = nid
2798
2799 if len(state) == 1:
2800 self.status = state
2801 self.level = None
2802 else:
2803 self.status = state[:1]
2804 self.level = state[1:]
2805 while self.level.startswith('0'):
2806 self.level = self.level[1:]
2807
2808 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002809 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002810 if info.status in ('R', 'C'):
2811 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002812 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002813 r[info.path] = info
2814 return r
2815 finally:
2816 p.Wait()
2817
2818 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002819 if self._bare:
2820 path = os.path.join(self._project.gitdir, HEAD)
2821 else:
2822 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002823 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002824 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002825 except IOError as e:
2826 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002827 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002828 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002829 finally:
2830 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302831 try:
2832 line = line.decode()
2833 except AttributeError:
2834 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002835 if line.startswith('ref: '):
2836 return line[5:-1]
2837 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002838
2839 def SetHead(self, ref, message=None):
2840 cmdv = []
2841 if message is not None:
2842 cmdv.extend(['-m', message])
2843 cmdv.append(HEAD)
2844 cmdv.append(ref)
2845 self.symbolic_ref(*cmdv)
2846
2847 def DetachHead(self, new, message=None):
2848 cmdv = ['--no-deref']
2849 if message is not None:
2850 cmdv.extend(['-m', message])
2851 cmdv.append(HEAD)
2852 cmdv.append(new)
2853 self.update_ref(*cmdv)
2854
2855 def UpdateRef(self, name, new, old=None,
2856 message=None,
2857 detach=False):
2858 cmdv = []
2859 if message is not None:
2860 cmdv.extend(['-m', message])
2861 if detach:
2862 cmdv.append('--no-deref')
2863 cmdv.append(name)
2864 cmdv.append(new)
2865 if old is not None:
2866 cmdv.append(old)
2867 self.update_ref(*cmdv)
2868
2869 def DeleteRef(self, name, old=None):
2870 if not old:
2871 old = self.rev_parse(name)
2872 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002873 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002874
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002875 def rev_list(self, *args, **kw):
2876 if 'format' in kw:
2877 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2878 else:
2879 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002880 cmdv.extend(args)
2881 p = GitCommand(self._project,
2882 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002883 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002884 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002885 capture_stdout=True,
2886 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002887 r = []
2888 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002889 if line[-1] == '\n':
2890 line = line[:-1]
2891 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002893 raise GitError('%s rev-list %s: %s' %
2894 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002895 return r
2896
2897 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002898 """Allow arbitrary git commands using pythonic syntax.
2899
2900 This allows you to do things like:
2901 git_obj.rev_parse('HEAD')
2902
2903 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2904 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002905 Any other positional arguments will be passed to the git command, and the
2906 following keyword arguments are supported:
2907 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002908
2909 Args:
2910 name: The name of the git command to call. Any '_' characters will
2911 be replaced with '-'.
2912
2913 Returns:
2914 A callable object that will try to call git with the named command.
2915 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002916 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002917
Dave Borowitz091f8932012-10-23 17:01:04 -07002918 def runner(*args, **kwargs):
2919 cmdv = []
2920 config = kwargs.pop('config', None)
2921 for k in kwargs:
2922 raise TypeError('%s() got an unexpected keyword argument %r'
2923 % (name, k))
2924 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002925 if not git_require((1, 7, 2)):
2926 raise ValueError('cannot set config on command line for %s()'
2927 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302928 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002929 cmdv.append('-c')
2930 cmdv.append('%s=%s' % (k, v))
2931 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002932 cmdv.extend(args)
2933 p = GitCommand(self._project,
2934 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002935 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002936 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002937 capture_stdout=True,
2938 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002939 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002940 raise GitError('%s %s: %s' %
2941 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002942 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302943 try:
Conley Owensedd01512013-09-26 12:59:58 -07002944 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302945 except AttributeError:
2946 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002947 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2948 return r[:-1]
2949 return r
2950 return runner
2951
2952
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002953class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002954
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002955 def __str__(self):
2956 return 'prior sync failed; rebase still in progress'
2957
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002958
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002959class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002960
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002961 def __str__(self):
2962 return 'contains uncommitted changes'
2963
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002964
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002965class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002966
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002967 def __init__(self, project, text):
2968 self.project = project
2969 self.text = text
2970
2971 def Print(self, syncbuf):
2972 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2973 syncbuf.out.nl()
2974
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002975
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002976class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002977
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002978 def __init__(self, project, why):
2979 self.project = project
2980 self.why = why
2981
2982 def Print(self, syncbuf):
2983 syncbuf.out.fail('error: %s/: %s',
2984 self.project.relpath,
2985 str(self.why))
2986 syncbuf.out.nl()
2987
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002988
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002989class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002990
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002991 def __init__(self, project, action):
2992 self.project = project
2993 self.action = action
2994
2995 def Run(self, syncbuf):
2996 out = syncbuf.out
2997 out.project('project %s/', self.project.relpath)
2998 out.nl()
2999 try:
3000 self.action()
3001 out.nl()
3002 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003003 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004 out.nl()
3005 return False
3006
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003007
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003008class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003009
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003010 def __init__(self, config):
3011 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003012 self.project = self.printer('header', attr='bold')
3013 self.info = self.printer('info')
3014 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003015
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003016
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003017class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003018
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003019 def __init__(self, config, detach_head=False):
3020 self._messages = []
3021 self._failures = []
3022 self._later_queue1 = []
3023 self._later_queue2 = []
3024
3025 self.out = _SyncColoring(config)
3026 self.out.redirect(sys.stderr)
3027
3028 self.detach_head = detach_head
3029 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003030 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003031
3032 def info(self, project, fmt, *args):
3033 self._messages.append(_InfoMessage(project, fmt % args))
3034
3035 def fail(self, project, err=None):
3036 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003037 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003038
3039 def later1(self, project, what):
3040 self._later_queue1.append(_Later(project, what))
3041
3042 def later2(self, project, what):
3043 self._later_queue2.append(_Later(project, what))
3044
3045 def Finish(self):
3046 self._PrintMessages()
3047 self._RunLater()
3048 self._PrintMessages()
3049 return self.clean
3050
David Rileye0684ad2017-04-05 00:02:59 -07003051 def Recently(self):
3052 recent_clean = self.recent_clean
3053 self.recent_clean = True
3054 return recent_clean
3055
3056 def _MarkUnclean(self):
3057 self.clean = False
3058 self.recent_clean = False
3059
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003060 def _RunLater(self):
3061 for q in ['_later_queue1', '_later_queue2']:
3062 if not self._RunQueue(q):
3063 return
3064
3065 def _RunQueue(self, queue):
3066 for m in getattr(self, queue):
3067 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003068 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003069 return False
3070 setattr(self, queue, [])
3071 return True
3072
3073 def _PrintMessages(self):
3074 for m in self._messages:
3075 m.Print(self)
3076 for m in self._failures:
3077 m.Print(self)
3078
3079 self._messages = []
3080 self._failures = []
3081
3082
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003083class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003084
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003085 """A special project housed under .repo.
3086 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003087
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003088 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003089 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003090 manifest=manifest,
3091 name=name,
3092 gitdir=gitdir,
3093 objdir=gitdir,
3094 worktree=worktree,
3095 remote=RemoteSpec('origin'),
3096 relpath='.repo/%s' % name,
3097 revisionExpr='refs/heads/master',
3098 revisionId=None,
3099 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003100
3101 def PreSync(self):
3102 if self.Exists:
3103 cb = self.CurrentBranch
3104 if cb:
3105 base = self.GetBranch(cb).merge
3106 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003107 self.revisionExpr = base
3108 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003109
Martin Kelly224a31a2017-07-10 14:46:25 -07003110 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003111 """ Prepare MetaProject for manifest branch switch
3112 """
3113
3114 # detach and delete manifest branch, allowing a new
3115 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003116 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003117 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003118 syncbuf.Finish()
3119
3120 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003121 ['update-ref', '-d', 'refs/heads/default'],
3122 capture_stdout=True,
3123 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003124
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003125 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003126 def LastFetch(self):
3127 try:
3128 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3129 return os.path.getmtime(fh)
3130 except OSError:
3131 return 0
3132
3133 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003134 def HasChanges(self):
3135 """Has the remote received new commits not yet checked out?
3136 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003137 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003138 return False
3139
David Pursehouse8a68ff92012-09-24 12:15:13 +09003140 all_refs = self.bare_ref.all
3141 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003142 head = self.work_git.GetHead()
3143 if head.startswith(R_HEADS):
3144 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003145 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003146 except KeyError:
3147 head = None
3148
3149 if revid == head:
3150 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003151 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003152 return True
3153 return False