blob: 5ed103b9f078a85595063b5b44b688eb34240bee [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
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080015import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070016import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070017import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import os
LaMont Jonesd82be3e2022-04-05 19:30:46 +000019import platform
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
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.parse
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
LaMont Jones55ee3042022-04-06 17:10:21 +000035from git_trace2_event_log import EventLog
Remy Bohmer16c13282020-09-10 10:38:04 +020036from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040037from error import ManifestInvalidRevisionError, ManifestInvalidPathError
LaMont Jones409407a2022-04-05 21:21:56 +000038from error import NoManifestException, ManifestParseError
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040040import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040041from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050043from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070045
George Engelbrecht9bc283e2020-04-02 12:36:09 -060046# Maximum sleep time allowed during retries.
47MAXIMUM_RETRY_SLEEP_SEC = 3600.0
48# +-10% random jitter is added to each Fetches retry sleep duration.
49RETRY_JITTER_PERCENT = 0.1
50
51
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070052def _lwrite(path, content):
53 lock = '%s.lock' % path
54
Remy Bohmer169b0212020-11-21 10:57:52 +010055 # Maintain Unix line endings on all OS's to match git behavior.
56 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070057 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070058
59 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070060 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080062 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070063 raise
64
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070065
Shawn O. Pearce48244782009-04-16 08:25:57 -070066def _error(fmt, *args):
67 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070068 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070069
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
David Pursehousef33929d2015-08-24 14:39:14 +090071def _warn(fmt, *args):
72 msg = fmt % args
73 print('warn: %s' % msg, file=sys.stderr)
74
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070076def not_rev(r):
77 return '^' + r
78
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070079
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080080def sq(r):
81 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080082
David Pursehouse819827a2020-02-12 15:20:19 +090083
Jonathan Nieder93719792015-03-17 11:29:58 -070084_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
86
Jonathan Nieder93719792015-03-17 11:29:58 -070087def _ProjectHooks():
88 """List the hooks present in the 'hooks' directory.
89
90 These hooks are project hooks and are copied to the '.git/hooks' directory
91 of all subprojects.
92
93 This function caches the list of hooks (based on the contents of the
94 'repo/hooks' directory) on the first call.
95
96 Returns:
97 A list of absolute paths to all of the files in the hooks directory.
98 """
99 global _project_hook_list
100 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700101 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700102 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700103 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700104 return _project_hook_list
105
106
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700107class DownloadedChange(object):
108 _commit_cache = None
109
110 def __init__(self, project, base, change_id, ps_id, commit):
111 self.project = project
112 self.base = base
113 self.change_id = change_id
114 self.ps_id = ps_id
115 self.commit = commit
116
117 @property
118 def commits(self):
119 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700120 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
121 '--abbrev-commit',
122 '--pretty=oneline',
123 '--reverse',
124 '--date-order',
125 not_rev(self.base),
126 self.commit,
127 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700128 return self._commit_cache
129
130
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700131class ReviewableBranch(object):
132 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400133 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134
135 def __init__(self, project, branch, base):
136 self.project = project
137 self.branch = branch
138 self.base = base
139
140 @property
141 def name(self):
142 return self.branch.name
143
144 @property
145 def commits(self):
146 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400147 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
148 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
149 try:
150 self._commit_cache = self.project.bare_git.rev_list(*args)
151 except GitError:
152 # We weren't able to probe the commits for this branch. Was it tracking
153 # a branch that no longer exists? If so, return no commits. Otherwise,
154 # rethrow the error as we don't know what's going on.
155 if self.base_exists:
156 raise
157
158 self._commit_cache = []
159
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700160 return self._commit_cache
161
162 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800163 def unabbrev_commits(self):
164 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700165 for commit in self.project.bare_git.rev_list(not_rev(self.base),
166 R_HEADS + self.name,
167 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800168 r[commit[0:8]] = commit
169 return r
170
171 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700173 return self.project.bare_git.log('--pretty=format:%cd',
174 '-n', '1',
175 R_HEADS + self.name,
176 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177
Mike Frysinger6da17752019-09-11 18:43:17 -0400178 @property
179 def base_exists(self):
180 """Whether the branch we're tracking exists.
181
182 Normally it should, but sometimes branches we track can get deleted.
183 """
184 if self._base_exists is None:
185 try:
186 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
187 # If we're still here, the base branch exists.
188 self._base_exists = True
189 except GitError:
190 # If we failed to verify, the base branch doesn't exist.
191 self._base_exists = False
192
193 return self._base_exists
194
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700195 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500196 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700197 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500198 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500199 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200200 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700201 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200202 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200203 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800204 validate_certs=True,
205 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500206 self.project.UploadForReview(branch=self.name,
207 people=people,
208 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700209 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500210 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500211 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700213 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200214 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200215 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800216 validate_certs=validate_certs,
217 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700219 def GetPublishedRefs(self):
220 refs = {}
221 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700222 self.branch.remote.SshReviewUrl(self.project.UserEmail),
223 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700224 for line in output.split('\n'):
225 try:
226 (sha, ref) = line.split()
227 refs[sha] = ref
228 except ValueError:
229 pass
230
231 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500237 super().__init__(config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100238 self.project = self.printer('header', attr='bold')
239 self.branch = self.printer('header', attr='bold')
240 self.nobranch = self.printer('nobranch', fg='red')
241 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
Anthony King7bdac712014-07-16 12:56:40 +0100243 self.added = self.printer('added', fg='green')
244 self.changed = self.printer('changed', fg='red')
245 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246
247
248class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700249
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500251 super().__init__(config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100252 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400253 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700255
Jack Neus6ea0cae2021-07-20 20:52:33 +0000256class Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700257
James W. Mills24c13082012-04-12 15:04:13 -0500258 def __init__(self, name, value, keep):
259 self.name = name
260 self.value = value
261 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700262
Jack Neus6ea0cae2021-07-20 20:52:33 +0000263 def __eq__(self, other):
264 if not isinstance(other, Annotation):
265 return False
266 return self.__dict__ == other.__dict__
267
268 def __lt__(self, other):
269 # This exists just so that lists of Annotation objects can be sorted, for
270 # use in comparisons.
271 if not isinstance(other, Annotation):
272 raise ValueError('comparison is not between two Annotation objects')
273 if self.name == other.name:
274 if self.value == other.value:
275 return self.keep < other.keep
276 return self.value < other.value
277 return self.name < other.name
278
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700279
Mike Frysingere6a202f2019-08-02 15:57:57 -0400280def _SafeExpandPath(base, subpath, skipfinal=False):
281 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700282
Mike Frysingere6a202f2019-08-02 15:57:57 -0400283 We make sure no intermediate symlinks are traversed, and that the final path
284 is not a special file (e.g. not a socket or fifo).
285
286 NB: We rely on a number of paths already being filtered out while parsing the
287 manifest. See the validation logic in manifest_xml.py for more details.
288 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500289 # Split up the path by its components. We can't use os.path.sep exclusively
290 # as some platforms (like Windows) will convert / to \ and that bypasses all
291 # our constructed logic here. Especially since manifest authors only use
292 # / in their paths.
293 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
294 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400295 if skipfinal:
296 # Whether the caller handles the final component itself.
297 finalpart = components.pop()
298
299 path = base
300 for part in components:
301 if part in {'.', '..'}:
302 raise ManifestInvalidPathError(
303 '%s: "%s" not allowed in paths' % (subpath, part))
304
305 path = os.path.join(path, part)
306 if platform_utils.islink(path):
307 raise ManifestInvalidPathError(
308 '%s: traversing symlinks not allow' % (path,))
309
310 if os.path.exists(path):
311 if not os.path.isfile(path) and not platform_utils.isdir(path):
312 raise ManifestInvalidPathError(
313 '%s: only regular files & directories allowed' % (path,))
314
315 if skipfinal:
316 path = os.path.join(path, finalpart)
317
318 return path
319
320
321class _CopyFile(object):
322 """Container for <copyfile> manifest element."""
323
324 def __init__(self, git_worktree, src, topdir, dest):
325 """Register a <copyfile> request.
326
327 Args:
328 git_worktree: Absolute path to the git project checkout.
329 src: Relative path under |git_worktree| of file to read.
330 topdir: Absolute path to the top of the repo client checkout.
331 dest: Relative path under |topdir| of file to write.
332 """
333 self.git_worktree = git_worktree
334 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700335 self.src = src
336 self.dest = dest
337
338 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400339 src = _SafeExpandPath(self.git_worktree, self.src)
340 dest = _SafeExpandPath(self.topdir, self.dest)
341
342 if platform_utils.isdir(src):
343 raise ManifestInvalidPathError(
344 '%s: copying from directory not supported' % (self.src,))
345 if platform_utils.isdir(dest):
346 raise ManifestInvalidPathError(
347 '%s: copying to directory not allowed' % (self.dest,))
348
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700349 # copy file if it does not exist or is out of date
350 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
351 try:
352 # remove existing file first, since it might be read-only
353 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800354 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400355 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200356 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700357 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200358 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700359 shutil.copy(src, dest)
360 # make the file read-only
361 mode = os.stat(dest)[stat.ST_MODE]
362 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
363 os.chmod(dest, mode)
364 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700365 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700366
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700367
Anthony King7bdac712014-07-16 12:56:40 +0100368class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400369 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700370
Mike Frysingere6a202f2019-08-02 15:57:57 -0400371 def __init__(self, git_worktree, src, topdir, dest):
372 """Register a <linkfile> request.
373
374 Args:
375 git_worktree: Absolute path to the git project checkout.
376 src: Target of symlink relative to path under |git_worktree|.
377 topdir: Absolute path to the top of the repo client checkout.
378 dest: Relative path under |topdir| of symlink to create.
379 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700380 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400381 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500382 self.src = src
383 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500384
Wink Saville4c426ef2015-06-03 08:05:17 -0700385 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500386 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700387 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500388 try:
389 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800390 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800391 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500392 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700393 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700394 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500395 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700396 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500397 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700398 _error('Cannot link file %s to %s', relSrc, absDest)
399
400 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400401 """Link the self.src & self.dest paths.
402
403 Handles wild cards on the src linking all of the files in the source in to
404 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700405 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500406 # Some people use src="." to create stable links to projects. Lets allow
407 # that but reject all other uses of "." to keep things simple.
408 if self.src == '.':
409 src = self.git_worktree
410 else:
411 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400412
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300413 if not glob.has_magic(src):
414 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400415 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
416 # dest & src are absolute paths at this point. Make sure the target of
417 # the symlink is relative in the context of the repo client checkout.
418 relpath = os.path.relpath(src, os.path.dirname(dest))
419 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700420 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400421 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300422 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400423 if os.path.exists(dest) and not platform_utils.isdir(dest):
424 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700425 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400426 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700427 # Create a releative path from source dir to destination dir
428 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400429 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700430
431 # Get the source file name
432 srcFile = os.path.basename(absSrcFile)
433
434 # Now form the final full paths to srcFile. They will be
435 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400436 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700437 relSrc = os.path.join(relSrcDir, srcFile)
438 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500439
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700440
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700441class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700442
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700443 def __init__(self,
444 name,
Anthony King7bdac712014-07-16 12:56:40 +0100445 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700446 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100447 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700448 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700449 orig_name=None,
450 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700451 self.name = name
452 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700453 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700454 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100455 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700456 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700457 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700458
Ian Kasprzak0286e312021-02-05 10:06:18 -0800459
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600461 # These objects can be shared between several working trees.
Mike Frysinger41289c62021-12-20 17:30:33 -0500462 shareable_dirs = ['hooks', 'objects', 'rr-cache']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700463
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700464 def __init__(self,
465 manifest,
466 name,
467 remote,
468 gitdir,
David James8d201162013-10-11 17:03:19 -0700469 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700470 worktree,
471 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700472 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800473 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100474 rebase=True,
475 groups=None,
476 sync_c=False,
477 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900478 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100479 clone_depth=None,
480 upstream=None,
481 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500482 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100483 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900484 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700485 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600486 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700487 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800488 """Init a Project object.
489
490 Args:
491 manifest: The XmlManifest object.
492 name: The `name` attribute of manifest.xml's project element.
493 remote: RemoteSpec object specifying its remote's properties.
494 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700495 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800496 worktree: Absolute path of git working tree.
497 relpath: Relative path of git working tree to repo's top directory.
498 revisionExpr: The `revision` attribute of manifest.xml's project element.
499 revisionId: git commit id for checking out.
500 rebase: The `rebase` attribute of manifest.xml's project element.
501 groups: The `groups` attribute of manifest.xml's project element.
502 sync_c: The `sync-c` attribute of manifest.xml's project element.
503 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900504 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800505 upstream: The `upstream` attribute of manifest.xml's project element.
506 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500507 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800508 is_derived: False if the project was explicitly defined in the manifest;
509 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400510 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900511 optimized_fetch: If True, when a project is set to a sha1 revision, only
512 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600513 retry_fetches: Retry remote fetches n times upon receiving transient error
514 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700515 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800516 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400517 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518 self.name = name
519 self.remote = remote
Michael Kelly37c21c22020-06-13 02:10:40 -0700520 self.UpdatePaths(relpath, worktree, gitdir, objdir)
Michael Kelly2f3c3312020-07-21 19:40:38 -0700521 self.SetRevision(revisionExpr, revisionId=revisionId)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700522
Mike Pontillod3153822012-02-28 11:53:24 -0800523 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700524 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700525 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800526 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900527 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900528 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700529 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800530 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500531 # NB: Do not use this setting in __init__ to change behavior so that the
532 # manifest.git checkout can inspect & change it after instantiating. See
533 # the XmlManifest init code for more info.
534 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800535 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900536 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600537 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800538 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800539
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700540 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700541 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500542 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500543 self.annotations = []
Bryan Jacobsf609f912013-05-06 13:36:24 -0400544 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700545 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700546
Doug Anderson37282b42011-03-04 11:54:18 -0800547 # This will be filled in if a project is later identified to be the
548 # project containing repo hooks.
549 self.enabled_repo_hooks = []
550
LaMont Jonescc879a92021-11-18 22:40:18 +0000551 def RelPath(self, local=True):
552 """Return the path for the project relative to a manifest.
553
554 Args:
555 local: a boolean, if True, the path is relative to the local
556 (sub)manifest. If false, the path is relative to the
557 outermost manifest.
558 """
559 if local:
560 return self.relpath
561 return os.path.join(self.manifest.path_prefix, self.relpath)
562
Michael Kelly2f3c3312020-07-21 19:40:38 -0700563 def SetRevision(self, revisionExpr, revisionId=None):
564 """Set revisionId based on revision expression and id"""
565 self.revisionExpr = revisionExpr
566 if revisionId is None and revisionExpr and IsId(revisionExpr):
567 self.revisionId = self.revisionExpr
568 else:
569 self.revisionId = revisionId
570
Michael Kelly37c21c22020-06-13 02:10:40 -0700571 def UpdatePaths(self, relpath, worktree, gitdir, objdir):
572 """Update paths used by this project"""
573 self.gitdir = gitdir.replace('\\', '/')
574 self.objdir = objdir.replace('\\', '/')
575 if worktree:
576 self.worktree = os.path.normpath(worktree).replace('\\', '/')
577 else:
578 self.worktree = None
579 self.relpath = relpath
580
581 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
582 defaults=self.manifest.globalConfig)
583
584 if self.worktree:
585 self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir)
586 else:
587 self.work_git = None
588 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir)
589 self.bare_ref = GitRefs(self.gitdir)
590 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
591
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700592 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800593 def Derived(self):
594 return self.is_derived
595
596 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700597 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700598 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599
600 @property
601 def CurrentBranch(self):
602 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400603
604 The branch name omits the 'refs/heads/' prefix.
605 None is returned if the project is on a detached HEAD, or if the work_git is
606 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700607 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400608 try:
609 b = self.work_git.GetHead()
610 except NoManifestException:
611 # If the local checkout is in a bad state, don't barf. Let the callers
612 # process this like the head is unreadable.
613 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700614 if b.startswith(R_HEADS):
615 return b[len(R_HEADS):]
616 return None
617
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700618 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500619 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
620 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
621 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200622
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700623 def IsDirty(self, consider_untracked=True):
624 """Is the working directory modified in some way?
625 """
626 self.work_git.update_index('-q',
627 '--unmerged',
628 '--ignore-missing',
629 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900630 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700631 return True
632 if self.work_git.DiffZ('diff-files'):
633 return True
634 if consider_untracked and self.work_git.LsOthers():
635 return True
636 return False
637
638 _userident_name = None
639 _userident_email = None
640
641 @property
642 def UserName(self):
643 """Obtain the user's personal name.
644 """
645 if self._userident_name is None:
646 self._LoadUserIdentity()
647 return self._userident_name
648
649 @property
650 def UserEmail(self):
651 """Obtain the user's email address. This is very likely
652 to be their Gerrit login.
653 """
654 if self._userident_email is None:
655 self._LoadUserIdentity()
656 return self._userident_email
657
658 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900659 u = self.bare_git.var('GIT_COMMITTER_IDENT')
660 m = re.compile("^(.*) <([^>]*)> ").match(u)
661 if m:
662 self._userident_name = m.group(1)
663 self._userident_email = m.group(2)
664 else:
665 self._userident_name = ''
666 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700667
668 def GetRemote(self, name):
669 """Get the configuration for a single remote.
670 """
671 return self.config.GetRemote(name)
672
673 def GetBranch(self, name):
674 """Get the configuration for a single branch.
675 """
676 return self.config.GetBranch(name)
677
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700678 def GetBranches(self):
679 """Get all existing local branches.
680 """
681 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900682 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700683 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700684
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530685 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700686 if name.startswith(R_HEADS):
687 name = name[len(R_HEADS):]
688 b = self.GetBranch(name)
689 b.current = name == current
690 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900691 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700692 heads[name] = b
693
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530694 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700695 if name.startswith(R_PUB):
696 name = name[len(R_PUB):]
697 b = heads.get(name)
698 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900699 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700700
701 return heads
702
Colin Cross5acde752012-03-28 20:15:45 -0700703 def MatchesGroups(self, manifest_groups):
704 """Returns true if the manifest groups specified at init should cause
705 this project to be synced.
706 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700707 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700708
709 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700710 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700711 manifest_groups: "-group1,group2"
712 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500713
714 The special manifest group "default" will match any project that
715 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700716 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500717 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700718 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700719 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500720 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700721
Conley Owens971de8e2012-04-16 10:36:08 -0700722 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700723 for group in expanded_manifest_groups:
724 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700725 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700726 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700727 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700728
Conley Owens971de8e2012-04-16 10:36:08 -0700729 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700731# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700732 def UncommitedFiles(self, get_all=True):
733 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700735 Args:
736 get_all: a boolean, if True - get information about all different
737 uncommitted files. If False - return as soon as any kind of
738 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500739 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700740 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500741 self.work_git.update_index('-q',
742 '--unmerged',
743 '--ignore-missing',
744 '--refresh')
745 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700746 details.append("rebase in progress")
747 if not get_all:
748 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500749
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700750 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
751 if changes:
752 details.extend(changes)
753 if not get_all:
754 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500755
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700756 changes = self.work_git.DiffZ('diff-files').keys()
757 if changes:
758 details.extend(changes)
759 if not get_all:
760 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500761
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700762 changes = self.work_git.LsOthers()
763 if changes:
764 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500765
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700766 return details
767
768 def HasChanges(self):
769 """Returns true if there are uncommitted changes.
770 """
771 if self.UncommitedFiles(get_all=False):
772 return True
773 else:
774 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500775
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600776 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700777 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200778
779 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200780 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600781 quiet: If True then only print the project name. Do not print
782 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700783 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700784 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700785 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200786 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700787 print(file=output_redir)
788 print('project %s/' % self.relpath, file=output_redir)
789 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 return
791
792 self.work_git.update_index('-q',
793 '--unmerged',
794 '--ignore-missing',
795 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700796 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700797 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
798 df = self.work_git.DiffZ('diff-files')
799 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100800 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700801 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700802
803 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700804 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200805 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700806 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700807
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600808 if quiet:
809 out.nl()
810 return 'DIRTY'
811
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812 branch = self.CurrentBranch
813 if branch is None:
814 out.nobranch('(*** NO BRANCH ***)')
815 else:
816 out.branch('branch %s', branch)
817 out.nl()
818
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700819 if rb:
820 out.important('prior sync failed; rebase still in progress')
821 out.nl()
822
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823 paths = list()
824 paths.extend(di.keys())
825 paths.extend(df.keys())
826 paths.extend(do)
827
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530828 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900829 try:
830 i = di[p]
831 except KeyError:
832 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700833
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900834 try:
835 f = df[p]
836 except KeyError:
837 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200838
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900839 if i:
840 i_status = i.status.upper()
841 else:
842 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900844 if f:
845 f_status = f.status.lower()
846 else:
847 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848
849 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800850 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700851 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700852 else:
853 line = ' %s%s\t%s' % (i_status, f_status, p)
854
855 if i and not f:
856 out.added('%s', line)
857 elif (i and f) or (not i and f):
858 out.changed('%s', line)
859 elif not i and not f:
860 out.untracked('%s', line)
861 else:
862 out.write('%s', line)
863 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200864
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700865 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700866
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500867 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868 """Prints the status of the repository to stdout.
869 """
870 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500871 if output_redir:
872 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 cmd = ['diff']
874 if out.is_on:
875 cmd.append('--color')
876 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300877 if absolute_paths:
878 cmd.append('--src-prefix=a/%s/' % self.relpath)
879 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400881 try:
882 p = GitCommand(self,
883 cmd,
884 capture_stdout=True,
885 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500886 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400887 except GitError as e:
888 out.nl()
889 out.project('project %s/' % self.relpath)
890 out.nl()
891 out.fail('%s', str(e))
892 out.nl()
893 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500894 if p.stdout:
895 out.nl()
896 out.project('project %s/' % self.relpath)
897 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500898 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400899 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700900
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700901# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900902 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903 """Was the branch published (uploaded) for code review?
904 If so, returns the SHA-1 hash of the last published
905 state for the branch.
906 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700907 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900908 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700909 try:
910 return self.bare_git.rev_parse(key)
911 except GitError:
912 return None
913 else:
914 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900915 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700916 except KeyError:
917 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700918
David Pursehouse8a68ff92012-09-24 12:15:13 +0900919 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920 """Prunes any stale published refs.
921 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900922 if all_refs is None:
923 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700924 heads = set()
925 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530926 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 if name.startswith(R_HEADS):
928 heads.add(name)
929 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900930 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700931
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530932 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933 n = name[len(R_PUB):]
934 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900935 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700937 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938 """List any branches which can be uploaded for review.
939 """
940 heads = {}
941 pubed = {}
942
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530943 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900945 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700946 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900947 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700948
949 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530950 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900951 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700953 if selected_branch and branch != selected_branch:
954 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800956 rb = self.GetUploadableBranch(branch)
957 if rb:
958 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700959 return ready
960
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800961 def GetUploadableBranch(self, branch_name):
962 """Get a single uploadable branch, or None.
963 """
964 branch = self.GetBranch(branch_name)
965 base = branch.LocalMerge
966 if branch.LocalMerge:
967 rb = ReviewableBranch(self, branch, base)
968 if rb.commits:
969 return rb
970 return None
971
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700972 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100973 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500974 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700975 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500976 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500977 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200978 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700979 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200980 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200981 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800982 validate_certs=True,
983 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700984 """Uploads the named branch for code review.
985 """
986 if branch is None:
987 branch = self.CurrentBranch
988 if branch is None:
989 raise GitError('not currently on a branch')
990
991 branch = self.GetBranch(branch)
992 if not branch.LocalMerge:
993 raise GitError('branch %s does not track a remote' % branch.name)
994 if not branch.remote.review:
995 raise GitError('remote %s has no review url' % branch.remote.name)
996
Bryan Jacobsf609f912013-05-06 13:36:24 -0400997 if dest_branch is None:
998 dest_branch = self.dest_branch
999 if dest_branch is None:
1000 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001 if not dest_branch.startswith(R_HEADS):
1002 dest_branch = R_HEADS + dest_branch
1003
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001004 if not branch.remote.projectname:
1005 branch.remote.projectname = self.name
1006 branch.remote.Save()
1007
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001008 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001009 if url is None:
1010 raise UploadError('review not configured')
1011 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -05001012 if dryrun:
1013 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001014
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001015 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001016 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001017
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001018 for push_option in (push_options or []):
1019 cmd.append('-o')
1020 cmd.append(push_option)
1021
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001022 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001023
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001024 if dest_branch.startswith(R_HEADS):
1025 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001026
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001027 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001028 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001029 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001030 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001031 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001032 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001033
David Pursehousef25a3702018-11-14 19:01:22 -08001034 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001035 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001036 if notify:
1037 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001038 if private:
1039 opts += ['private']
1040 if wip:
1041 opts += ['wip']
1042 if opts:
1043 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001044 cmd.append(ref_spec)
1045
Anthony King7bdac712014-07-16 12:56:40 +01001046 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001047 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
Mike Frysingerd7f86832020-11-19 19:18:46 -05001049 if not dryrun:
1050 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1051 self.bare_git.UpdateRef(R_PUB + branch.name,
1052 R_HEADS + branch.name,
1053 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001055# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001056 def _ExtractArchive(self, tarpath, path=None):
1057 """Extract the given tar on its current location
1058
1059 Args:
1060 - tarpath: The path to the actual tar file
1061
1062 """
1063 try:
1064 with tarfile.open(tarpath, 'r') as tar:
1065 tar.extractall(path=path)
1066 return True
1067 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001068 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001069 return False
1070
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001071 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001072 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001073 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001074 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001075 is_new=None,
Mike Frysinger73561142021-05-03 01:10:09 -04001076 current_branch_only=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001077 force_sync=False,
1078 clone_bundle=True,
Mike Frysingerd68ed632021-05-03 01:21:35 -04001079 tags=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001080 archive=False,
1081 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001082 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001083 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001084 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001085 ssh_proxy=None,
Raman Tennetif32f2432021-04-12 20:57:25 -07001086 clone_filter=None,
Raman Tenneticd89ec12021-04-22 09:18:14 -07001087 partial_clone_exclude=set()):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 """Perform only the network IO portion of the sync process.
1089 Local working directory/branch state is not affected.
1090 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001091 if archive and not isinstance(self, MetaProject):
1092 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001093 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001094 return False
1095
1096 name = self.relpath.replace('\\', '/')
1097 name = name.replace('/', '_')
1098 tarpath = '%s.tar' % name
1099 topdir = self.manifest.topdir
1100
1101 try:
1102 self._FetchArchive(tarpath, cwd=topdir)
1103 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001104 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001105 return False
1106
1107 # From now on, we only need absolute tarpath
1108 tarpath = os.path.join(topdir, tarpath)
1109
1110 if not self._ExtractArchive(tarpath, path=topdir):
1111 return False
1112 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001113 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001114 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001115 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001116 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001117 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001118
1119 # If the shared object dir already exists, don't try to rebootstrap with a
1120 # clone bundle download. We should have the majority of objects already.
1121 if clone_bundle and os.path.exists(self.objdir):
1122 clone_bundle = False
1123
Raman Tennetif32f2432021-04-12 20:57:25 -07001124 if self.name in partial_clone_exclude:
1125 clone_bundle = True
1126 clone_filter = None
1127
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001128 if is_new is None:
1129 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001130 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001131 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001132 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001133 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001134 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001135
1136 if is_new:
Mike Frysinger152032c2021-12-20 21:17:43 -05001137 alt = os.path.join(self.objdir, 'objects/info/alternates')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001138 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001139 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001140 # This works for both absolute and relative alternate directories.
1141 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001142 except IOError:
1143 alt_dir = None
1144 else:
1145 alt_dir = None
1146
Mike Frysingere50b6a72020-02-19 01:45:48 -05001147 if (clone_bundle
1148 and alt_dir is None
1149 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001150 is_new = False
1151
Mike Frysinger73561142021-05-03 01:10:09 -04001152 if current_branch_only is None:
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001153 if self.sync_c:
1154 current_branch_only = True
1155 elif not self.manifest._loaded:
1156 # Manifest cannot check defaults until it syncs.
1157 current_branch_only = False
1158 elif self.manifest.default.sync_c:
1159 current_branch_only = True
1160
Mike Frysingerd68ed632021-05-03 01:21:35 -04001161 if tags is None:
1162 tags = self.sync_tags
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001163
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001164 if self.clone_depth:
1165 depth = self.clone_depth
1166 else:
LaMont Jonesd82be3e2022-04-05 19:30:46 +00001167 depth = self.manifest.manifestProject.depth
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001168
Mike Frysinger521d01b2020-02-17 01:51:49 -05001169 # See if we can skip the network fetch entirely.
1170 if not (optimized_fetch and
1171 (ID_RE.match(self.revisionExpr) and
1172 self._CheckForImmutableRevision())):
1173 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001174 initial=is_new,
1175 quiet=quiet, verbose=verbose, output_redir=output_redir,
1176 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001177 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001178 submodules=submodules, force_sync=force_sync,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001179 ssh_proxy=ssh_proxy,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001180 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001181 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001182
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001183 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +00001184 dissociate = mp.dissociate
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001185 if dissociate:
Mike Frysinger152032c2021-12-20 21:17:43 -05001186 alternates_file = os.path.join(self.objdir, 'objects/info/alternates')
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001187 if os.path.exists(alternates_file):
1188 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001189 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1190 merge_output=bool(output_redir))
1191 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001192 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001193 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001194 return False
1195 platform_utils.remove(alternates_file)
1196
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001197 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001198 self._InitMRef()
1199 else:
1200 self._InitMirrorHead()
Mike Frysinger9d96f582021-09-28 11:27:24 -04001201 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
1202 missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001204
1205 def PostRepoUpgrade(self):
1206 self._InitHooks()
1207
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001208 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001209 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001210 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001211 for copyfile in self.copyfiles:
1212 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001213 for linkfile in self.linkfiles:
1214 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001215
Julien Camperguedd654222014-01-09 16:21:37 +01001216 def GetCommitRevisionId(self):
1217 """Get revisionId of a commit.
1218
1219 Use this method instead of GetRevisionId to get the id of the commit rather
1220 than the id of the current git object (for example, a tag)
1221
1222 """
1223 if not self.revisionExpr.startswith(R_TAGS):
1224 return self.GetRevisionId(self._allrefs)
1225
1226 try:
1227 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1228 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001229 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1230 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001231
David Pursehouse8a68ff92012-09-24 12:15:13 +09001232 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001233 if self.revisionId:
1234 return self.revisionId
1235
1236 rem = self.GetRemote(self.remote.name)
1237 rev = rem.ToLocal(self.revisionExpr)
1238
David Pursehouse8a68ff92012-09-24 12:15:13 +09001239 if all_refs is not None and rev in all_refs:
1240 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001241
1242 try:
1243 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1244 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001245 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1246 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001247
Raman Tenneti6a872c92021-01-14 19:17:50 -08001248 def SetRevisionId(self, revisionId):
Xin Li0e776a52021-06-29 21:42:34 +00001249 if self.revisionExpr:
Xin Liaabf79d2021-04-29 01:50:38 -07001250 self.upstream = self.revisionExpr
1251
Raman Tenneti6a872c92021-01-14 19:17:50 -08001252 self.revisionId = revisionId
1253
Martin Kellye4e94d22017-03-21 16:05:12 -07001254 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001255 """Perform only the local IO portion of the sync process.
1256 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001257 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001258 if not os.path.exists(self.gitdir):
1259 syncbuf.fail(self,
1260 'Cannot checkout %s due to missing network sync; Run '
1261 '`repo sync -n %s` first.' %
1262 (self.name, self.name))
1263 return
1264
Martin Kellye4e94d22017-03-21 16:05:12 -07001265 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001266 all_refs = self.bare_ref.all
1267 self.CleanPublishedCache(all_refs)
1268 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001269
Mike Frysinger0458faa2021-03-10 23:35:44 -05001270 # Special case the root of the repo client checkout. Make sure it doesn't
1271 # contain files being checked out to dirs we don't allow.
1272 if self.relpath == '.':
1273 PROTECTED_PATHS = {'.repo'}
1274 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1275 bad_paths = paths & PROTECTED_PATHS
1276 if bad_paths:
1277 syncbuf.fail(self,
1278 'Refusing to checkout project that writes to protected '
1279 'paths: %s' % (', '.join(bad_paths),))
1280 return
1281
David Pursehouse1d947b32012-10-25 12:23:11 +09001282 def _doff():
1283 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001284 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001285
Martin Kellye4e94d22017-03-21 16:05:12 -07001286 def _dosubmodules():
1287 self._SyncSubmodules(quiet=True)
1288
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001289 head = self.work_git.GetHead()
1290 if head.startswith(R_HEADS):
1291 branch = head[len(R_HEADS):]
1292 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001293 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001294 except KeyError:
1295 head = None
1296 else:
1297 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001298
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001299 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001300 # Currently on a detached HEAD. The user is assumed to
1301 # not have any local modifications worth worrying about.
1302 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001303 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001304 syncbuf.fail(self, _PriorSyncFailedError())
1305 return
1306
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001307 if head == revid:
1308 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001309 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001310 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001311 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001312 # The copy/linkfile config may have changed.
1313 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001314 return
1315 else:
1316 lost = self._revlist(not_rev(revid), HEAD)
1317 if lost:
1318 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001319
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001320 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001321 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001322 if submodules:
1323 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001324 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001325 syncbuf.fail(self, e)
1326 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001327 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001328 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001329
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001330 if head == revid:
1331 # No changes; don't do anything further.
1332 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001333 # The copy/linkfile config may have changed.
1334 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001335 return
1336
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001337 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001338
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001339 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001340 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001341 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001342 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001343 syncbuf.info(self,
1344 "leaving %s; does not track upstream",
1345 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001346 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001347 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001348 if submodules:
1349 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001350 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001351 syncbuf.fail(self, e)
1352 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001353 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001354 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001355
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001356 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001357
1358 # See if we can perform a fast forward merge. This can happen if our
1359 # branch isn't in the exact same state as we last published.
1360 try:
1361 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1362 # Skip the published logic.
1363 pub = False
1364 except GitError:
1365 pub = self.WasPublished(branch.name, all_refs)
1366
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001367 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001368 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001369 if not_merged:
1370 if upstream_gain:
1371 # The user has published this branch and some of those
1372 # commits are not yet merged upstream. We do not want
1373 # to rewrite the published commits so we punt.
1374 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001375 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001376 "branch %s is published (but not merged) and is now "
1377 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001378 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001379 elif pub == head:
1380 # All published commits are merged, and thus we are a
1381 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001382 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001383 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001384 if submodules:
1385 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001386 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001387
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001388 # Examine the local commits not in the remote. Find the
1389 # last one attributed to this user, if any.
1390 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001391 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001392 last_mine = None
1393 cnt_mine = 0
1394 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001395 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001396 if committer_email == self.UserEmail:
1397 last_mine = commit_id
1398 cnt_mine += 1
1399
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001400 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001401 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001402
1403 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001404 syncbuf.fail(self, _DirtyError())
1405 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001406
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001407 # If the upstream switched on us, warn the user.
1408 #
1409 if branch.merge != self.revisionExpr:
1410 if branch.merge and self.revisionExpr:
1411 syncbuf.info(self,
1412 'manifest switched %s...%s',
1413 branch.merge,
1414 self.revisionExpr)
1415 elif branch.merge:
1416 syncbuf.info(self,
1417 'manifest no longer tracks %s',
1418 branch.merge)
1419
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001420 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001421 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001422 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001424 syncbuf.info(self,
1425 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001426 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001427
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001428 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001429 if not ID_RE.match(self.revisionExpr):
1430 # in case of manifest sync the revisionExpr might be a SHA1
1431 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001432 if not branch.merge.startswith('refs/'):
1433 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434 branch.Save()
1435
Mike Pontillod3153822012-02-28 11:53:24 -08001436 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001437 def _docopyandlink():
1438 self._CopyAndLinkFiles()
1439
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001440 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001441 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001442 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001443 if submodules:
1444 syncbuf.later2(self, _dosubmodules)
1445 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001446 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001447 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001448 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001449 if submodules:
1450 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001451 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001452 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001453 syncbuf.fail(self, e)
1454 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001456 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001457 if submodules:
1458 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001459
Mike Frysingere6a202f2019-08-02 15:57:57 -04001460 def AddCopyFile(self, src, dest, topdir):
1461 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001462
Mike Frysingere6a202f2019-08-02 15:57:57 -04001463 No filesystem changes occur here. Actual copying happens later on.
1464
1465 Paths should have basic validation run on them before being queued.
1466 Further checking will be handled when the actual copy happens.
1467 """
1468 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1469
1470 def AddLinkFile(self, src, dest, topdir):
1471 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1472
1473 No filesystem changes occur here. Actual linking happens later on.
1474
1475 Paths should have basic validation run on them before being queued.
1476 Further checking will be handled when the actual link happens.
1477 """
1478 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001479
James W. Mills24c13082012-04-12 15:04:13 -05001480 def AddAnnotation(self, name, value, keep):
Jack Neus6ea0cae2021-07-20 20:52:33 +00001481 self.annotations.append(Annotation(name, value, keep))
James W. Mills24c13082012-04-12 15:04:13 -05001482
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001483 def DownloadPatchSet(self, change_id, patch_id):
1484 """Download a single patch set of a single change to FETCH_HEAD.
1485 """
1486 remote = self.GetRemote(self.remote.name)
1487
1488 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001489 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001490 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001491 if GitCommand(self, cmd, bare=True).Wait() != 0:
1492 return None
1493 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001494 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001495 change_id,
1496 patch_id,
1497 self.bare_git.rev_parse('FETCH_HEAD'))
1498
Mike Frysingerc0d18662020-02-19 19:19:18 -05001499 def DeleteWorktree(self, quiet=False, force=False):
1500 """Delete the source checkout and any other housekeeping tasks.
1501
1502 This currently leaves behind the internal .repo/ cache state. This helps
1503 when switching branches or manifest changes get reverted as we don't have
1504 to redownload all the git objects. But we should do some GC at some point.
1505
1506 Args:
1507 quiet: Whether to hide normal messages.
1508 force: Always delete tree even if dirty.
1509
1510 Returns:
1511 True if the worktree was completely cleaned out.
1512 """
1513 if self.IsDirty():
1514 if force:
1515 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1516 (self.relpath,), file=sys.stderr)
1517 else:
1518 print('error: %s: Cannot remove project: uncommitted changes are '
1519 'present.\n' % (self.relpath,), file=sys.stderr)
1520 return False
1521
1522 if not quiet:
1523 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1524
1525 # Unlock and delink from the main worktree. We don't use git's worktree
1526 # remove because it will recursively delete projects -- we handle that
1527 # ourselves below. https://crbug.com/git/48
1528 if self.use_git_worktrees:
1529 needle = platform_utils.realpath(self.gitdir)
1530 # Find the git worktree commondir under .repo/worktrees/.
1531 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1532 assert output.startswith('worktree '), output
1533 commondir = output[9:]
1534 # Walk each of the git worktrees to see where they point.
1535 configs = os.path.join(commondir, 'worktrees')
1536 for name in os.listdir(configs):
1537 gitdir = os.path.join(configs, name, 'gitdir')
1538 with open(gitdir) as fp:
1539 relpath = fp.read().strip()
1540 # Resolve the checkout path and see if it matches this project.
1541 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1542 if fullpath == needle:
1543 platform_utils.rmtree(os.path.join(configs, name))
1544
1545 # Delete the .git directory first, so we're less likely to have a partially
1546 # working git repository around. There shouldn't be any git projects here,
1547 # so rmtree works.
1548
1549 # Try to remove plain files first in case of git worktrees. If this fails
1550 # for any reason, we'll fall back to rmtree, and that'll display errors if
1551 # it can't remove things either.
1552 try:
1553 platform_utils.remove(self.gitdir)
1554 except OSError:
1555 pass
1556 try:
1557 platform_utils.rmtree(self.gitdir)
1558 except OSError as e:
1559 if e.errno != errno.ENOENT:
1560 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1561 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1562 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1563 return False
1564
1565 # Delete everything under the worktree, except for directories that contain
1566 # another git project.
1567 dirs_to_remove = []
1568 failed = False
1569 for root, dirs, files in platform_utils.walk(self.worktree):
1570 for f in files:
1571 path = os.path.join(root, f)
1572 try:
1573 platform_utils.remove(path)
1574 except OSError as e:
1575 if e.errno != errno.ENOENT:
1576 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1577 failed = True
1578 dirs[:] = [d for d in dirs
1579 if not os.path.lexists(os.path.join(root, d, '.git'))]
1580 dirs_to_remove += [os.path.join(root, d) for d in dirs
1581 if os.path.join(root, d) not in dirs_to_remove]
1582 for d in reversed(dirs_to_remove):
1583 if platform_utils.islink(d):
1584 try:
1585 platform_utils.remove(d)
1586 except OSError as e:
1587 if e.errno != errno.ENOENT:
1588 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1589 failed = True
1590 elif not platform_utils.listdir(d):
1591 try:
1592 platform_utils.rmdir(d)
1593 except OSError as e:
1594 if e.errno != errno.ENOENT:
1595 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1596 failed = True
1597 if failed:
1598 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1599 file=sys.stderr)
1600 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1601 return False
1602
1603 # Try deleting parent dirs if they are empty.
1604 path = self.worktree
1605 while path != self.manifest.topdir:
1606 try:
1607 platform_utils.rmdir(path)
1608 except OSError as e:
1609 if e.errno != errno.ENOENT:
1610 break
1611 path = os.path.dirname(path)
1612
1613 return True
1614
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001615# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001616 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001617 """Create a new branch off the manifest's revision.
1618 """
Simran Basib9a1b732015-08-20 12:19:28 -07001619 if not branch_merge:
1620 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001621 head = self.work_git.GetHead()
1622 if head == (R_HEADS + name):
1623 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001624
David Pursehouse8a68ff92012-09-24 12:15:13 +09001625 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001626 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001627 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001628 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001629 capture_stdout=True,
1630 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001631
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001632 branch = self.GetBranch(name)
1633 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001634 branch.merge = branch_merge
1635 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1636 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001637
1638 if revision is None:
1639 revid = self.GetRevisionId(all_refs)
1640 else:
1641 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001642
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001643 if head.startswith(R_HEADS):
1644 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001645 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001646 except KeyError:
1647 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001648 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001649 ref = R_HEADS + name
1650 self.work_git.update_ref(ref, revid)
1651 self.work_git.symbolic_ref(HEAD, ref)
1652 branch.Save()
1653 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001654
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001655 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001656 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001657 capture_stdout=True,
1658 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001659 branch.Save()
1660 return True
1661 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001662
Wink Saville02d79452009-04-10 13:01:24 -07001663 def CheckoutBranch(self, name):
1664 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001665
1666 Args:
1667 name: The name of the branch to checkout.
1668
1669 Returns:
1670 True if the checkout succeeded; False if it didn't; None if the branch
1671 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001672 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001673 rev = R_HEADS + name
1674 head = self.work_git.GetHead()
1675 if head == rev:
1676 # Already on the branch
1677 #
1678 return True
Wink Saville02d79452009-04-10 13:01:24 -07001679
David Pursehouse8a68ff92012-09-24 12:15:13 +09001680 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001681 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001682 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001683 except KeyError:
1684 # Branch does not exist in this project
1685 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001686 return None
Wink Saville02d79452009-04-10 13:01:24 -07001687
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001688 if head.startswith(R_HEADS):
1689 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001690 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001691 except KeyError:
1692 head = None
1693
1694 if head == revid:
1695 # Same revision; just update HEAD to point to the new
1696 # target branch, but otherwise take no other action.
1697 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001698 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1699 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001700 return True
1701
1702 return GitCommand(self,
1703 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001704 capture_stdout=True,
1705 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001706
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001707 def AbandonBranch(self, name):
1708 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001709
1710 Args:
1711 name: The name of the branch to abandon.
1712
1713 Returns:
1714 True if the abandon succeeded; False if it didn't; None if the branch
1715 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001716 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001717 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001718 all_refs = self.bare_ref.all
1719 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001720 # Doesn't exist
1721 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001722
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001723 head = self.work_git.GetHead()
1724 if head == rev:
1725 # We can't destroy the branch while we are sitting
1726 # on it. Switch to a detached HEAD.
1727 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001728 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001729
David Pursehouse8a68ff92012-09-24 12:15:13 +09001730 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001731 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001732 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001733 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001734 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001735
1736 return GitCommand(self,
1737 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001738 capture_stdout=True,
1739 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001740
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001741 def PruneHeads(self):
1742 """Prune any topic branches already merged into upstream.
1743 """
1744 cb = self.CurrentBranch
1745 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001746 left = self._allrefs
1747 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001748 if name.startswith(R_HEADS):
1749 name = name[len(R_HEADS):]
1750 if cb is None or name != cb:
1751 kill.append(name)
1752
Mike Frysingera3794e92021-03-11 23:24:01 -05001753 # Minor optimization: If there's nothing to prune, then don't try to read
1754 # any project state.
1755 if not kill and not cb:
1756 return []
1757
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001758 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001759 if cb is not None \
1760 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001761 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001762 self.work_git.DetachHead(HEAD)
1763 kill.append(cb)
1764
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001765 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001766 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001767
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001768 try:
1769 self.bare_git.DetachHead(rev)
1770
1771 b = ['branch', '-d']
1772 b.extend(kill)
1773 b = GitCommand(self, b, bare=True,
1774 capture_stdout=True,
1775 capture_stderr=True)
1776 b.Wait()
1777 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001778 if ID_RE.match(old):
1779 self.bare_git.DetachHead(old)
1780 else:
1781 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001782 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001783
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001784 for branch in kill:
1785 if (R_HEADS + branch) not in left:
1786 self.CleanPublishedCache()
1787 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001788
1789 if cb and cb not in kill:
1790 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001791 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001792
1793 kept = []
1794 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001795 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001796 branch = self.GetBranch(branch)
1797 base = branch.LocalMerge
1798 if not base:
1799 base = rev
1800 kept.append(ReviewableBranch(self, branch, base))
1801 return kept
1802
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001803# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001804 def GetRegisteredSubprojects(self):
1805 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001806
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001807 def rec(subprojects):
1808 if not subprojects:
1809 return
1810 result.extend(subprojects)
1811 for p in subprojects:
1812 rec(p.subprojects)
1813 rec(self.subprojects)
1814 return result
1815
1816 def _GetSubmodules(self):
1817 # Unfortunately we cannot call `git submodule status --recursive` here
1818 # because the working tree might not exist yet, and it cannot be used
1819 # without a working tree in its current implementation.
1820
1821 def get_submodules(gitdir, rev):
1822 # Parse .gitmodules for submodule sub_paths and sub_urls
1823 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1824 if not sub_paths:
1825 return []
1826 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1827 # revision of submodule repository
1828 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1829 submodules = []
1830 for sub_path, sub_url in zip(sub_paths, sub_urls):
1831 try:
1832 sub_rev = sub_revs[sub_path]
1833 except KeyError:
1834 # Ignore non-exist submodules
1835 continue
1836 submodules.append((sub_rev, sub_path, sub_url))
1837 return submodules
1838
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001839 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1840 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001841
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001842 def parse_gitmodules(gitdir, rev):
1843 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1844 try:
Anthony King7bdac712014-07-16 12:56:40 +01001845 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1846 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001847 except GitError:
1848 return [], []
1849 if p.Wait() != 0:
1850 return [], []
1851
1852 gitmodules_lines = []
1853 fd, temp_gitmodules_path = tempfile.mkstemp()
1854 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001855 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001856 os.close(fd)
1857 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001858 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1859 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001860 if p.Wait() != 0:
1861 return [], []
1862 gitmodules_lines = p.stdout.split('\n')
1863 except GitError:
1864 return [], []
1865 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001866 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001867
1868 names = set()
1869 paths = {}
1870 urls = {}
1871 for line in gitmodules_lines:
1872 if not line:
1873 continue
1874 m = re_path.match(line)
1875 if m:
1876 names.add(m.group(1))
1877 paths[m.group(1)] = m.group(2)
1878 continue
1879 m = re_url.match(line)
1880 if m:
1881 names.add(m.group(1))
1882 urls[m.group(1)] = m.group(2)
1883 continue
1884 names = sorted(names)
1885 return ([paths.get(name, '') for name in names],
1886 [urls.get(name, '') for name in names])
1887
1888 def git_ls_tree(gitdir, rev, paths):
1889 cmd = ['ls-tree', rev, '--']
1890 cmd.extend(paths)
1891 try:
Anthony King7bdac712014-07-16 12:56:40 +01001892 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1893 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001894 except GitError:
1895 return []
1896 if p.Wait() != 0:
1897 return []
1898 objects = {}
1899 for line in p.stdout.split('\n'):
1900 if not line.strip():
1901 continue
1902 object_rev, object_path = line.split()[2:4]
1903 objects[object_path] = object_rev
1904 return objects
1905
1906 try:
1907 rev = self.GetRevisionId()
1908 except GitError:
1909 return []
1910 return get_submodules(self.gitdir, rev)
1911
1912 def GetDerivedSubprojects(self):
1913 result = []
1914 if not self.Exists:
1915 # If git repo does not exist yet, querying its submodules will
1916 # mess up its states; so return here.
1917 return result
1918 for rev, path, url in self._GetSubmodules():
1919 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001920 relpath, worktree, gitdir, objdir = \
1921 self.manifest.GetSubprojectPaths(self, name, path)
1922 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001923 if project:
1924 result.extend(project.GetDerivedSubprojects())
1925 continue
David James8d201162013-10-11 17:03:19 -07001926
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001927 if url.startswith('..'):
1928 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001929 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001930 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001931 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001932 review=self.remote.review,
1933 revision=self.remote.revision)
1934 subproject = Project(manifest=self.manifest,
1935 name=name,
1936 remote=remote,
1937 gitdir=gitdir,
1938 objdir=objdir,
1939 worktree=worktree,
1940 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001941 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001942 revisionId=rev,
1943 rebase=self.rebase,
1944 groups=self.groups,
1945 sync_c=self.sync_c,
1946 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001947 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001948 parent=self,
1949 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001950 result.append(subproject)
1951 result.extend(subproject.GetDerivedSubprojects())
1952 return result
1953
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001954# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001955 def EnableRepositoryExtension(self, key, value='true', version=1):
1956 """Enable git repository extension |key| with |value|.
1957
1958 Args:
1959 key: The extension to enabled. Omit the "extensions." prefix.
1960 value: The value to use for the extension.
1961 version: The minimum git repository version needed.
1962 """
1963 # Make sure the git repo version is new enough already.
1964 found_version = self.config.GetInt('core.repositoryFormatVersion')
1965 if found_version is None:
1966 found_version = 0
1967 if found_version < version:
1968 self.config.SetString('core.repositoryFormatVersion', str(version))
1969
1970 # Enable the extension!
1971 self.config.SetString('extensions.%s' % (key,), value)
1972
Mike Frysinger50a81de2020-09-06 15:51:21 -04001973 def ResolveRemoteHead(self, name=None):
1974 """Find out what the default branch (HEAD) points to.
1975
1976 Normally this points to refs/heads/master, but projects are moving to main.
1977 Support whatever the server uses rather than hardcoding "master" ourselves.
1978 """
1979 if name is None:
1980 name = self.remote.name
1981
1982 # The output will look like (NB: tabs are separators):
1983 # ref: refs/heads/master HEAD
1984 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1985 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1986
1987 for line in output.splitlines():
1988 lhs, rhs = line.split('\t', 1)
1989 if rhs == 'HEAD' and lhs.startswith('ref:'):
1990 return lhs[4:].strip()
1991
1992 return None
1993
Zac Livingstone4332262017-06-16 08:56:09 -06001994 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001995 try:
1996 # if revision (sha or tag) is not present then following function
1997 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001998 self.bare_git.rev_list('-1', '--missing=allow-any',
1999 '%s^0' % self.revisionExpr, '--')
Xin Li0e776a52021-06-29 21:42:34 +00002000 if self.upstream:
2001 rev = self.GetRemote(self.remote.name).ToLocal(self.upstream)
2002 self.bare_git.rev_list('-1', '--missing=allow-any',
2003 '%s^0' % rev, '--')
Xin Li8e983bb2021-07-20 20:15:30 +00002004 self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev)
Chris AtLee2fb64662014-01-16 21:32:33 -05002005 return True
2006 except GitError:
2007 # There is no such persistent revision. We have to fetch it.
2008 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002009
Julien Campergue335f5ef2013-10-16 11:02:35 +02002010 def _FetchArchive(self, tarpath, cwd=None):
2011 cmd = ['archive', '-v', '-o', tarpath]
2012 cmd.append('--remote=%s' % self.remote.url)
2013 cmd.append('--prefix=%s/' % self.relpath)
2014 cmd.append(self.revisionExpr)
2015
2016 command = GitCommand(self, cmd, cwd=cwd,
2017 capture_stdout=True,
2018 capture_stderr=True)
2019
2020 if command.Wait() != 0:
2021 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2022
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002023 def _RemoteFetch(self, name=None,
2024 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002025 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002026 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002027 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002028 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002029 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002030 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002031 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002032 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002033 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002034 ssh_proxy=None,
Xin Li745be2e2019-06-03 11:24:30 -07002035 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002036 clone_filter=None,
2037 retry_fetches=2,
2038 retry_sleep_initial_sec=4.0,
2039 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002040 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):
Robin Schneider9c1fc5b2021-11-13 22:55:32 +01002057 # This is a tag and its commit id should never change.
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002058 tag_name = self.revisionExpr[len(R_TAGS):]
Robin Schneider9c1fc5b2021-11-13 22:55:32 +01002059 elif self.upstream and self.upstream.startswith(R_TAGS):
2060 # This is a tag and its commit id should never change.
2061 tag_name = self.upstream[len(R_TAGS):]
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002062
2063 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002064 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002065 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002066 print('Skipped fetching project %s (already have persistent ref)'
2067 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002068 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002069 if is_sha1 and not depth:
2070 # When syncing a specific commit and --depth is not set:
2071 # * if upstream is explicitly specified and is not a sha1, fetch only
2072 # upstream as users expect only upstream to be fetch.
2073 # Note: The commit might not be in upstream in which case the sync
2074 # will fail.
2075 # * otherwise, fetch all branches to make sure we end up with the
2076 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002077 if self.upstream:
2078 current_branch_only = not ID_RE.match(self.upstream)
2079 else:
2080 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002082 if not name:
2083 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002084
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002085 remote = self.GetRemote(name)
Mike Frysinger339f2df2021-05-06 00:44:42 -04002086 if not remote.PreConnectFetch(ssh_proxy):
2087 ssh_proxy = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002088
Shawn O. Pearce88443382010-10-08 10:02:09 +02002089 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002090 if alt_dir and 'objects' == os.path.basename(alt_dir):
2091 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002092 packed_refs = os.path.join(self.gitdir, 'packed-refs')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002093
David Pursehouse8a68ff92012-09-24 12:15:13 +09002094 all_refs = self.bare_ref.all
2095 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096 tmp = set()
2097
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302098 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002099 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002101 all_refs[r] = ref_id
2102 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002103 continue
2104
David Pursehouse8a68ff92012-09-24 12:15:13 +09002105 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 continue
2107
David Pursehouse8a68ff92012-09-24 12:15:13 +09002108 r = 'refs/_alt/%s' % ref_id
2109 all_refs[r] = ref_id
2110 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111 tmp.add(r)
2112
heping3d7bbc92017-04-12 19:51:47 +08002113 tmp_packed_lines = []
2114 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002115
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302116 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002117 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002118 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002119 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002120 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121
heping3d7bbc92017-04-12 19:51:47 +08002122 tmp_packed = ''.join(tmp_packed_lines)
2123 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002124 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002126 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002127
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002128 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002129
Xin Li745be2e2019-06-03 11:24:30 -07002130 if clone_filter:
2131 git_require((2, 19, 0), fail=True, msg='partial clones')
2132 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002133 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002134
Conley Owensf97e8382015-01-21 11:12:46 -08002135 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002136 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002137 else:
2138 # If this repo has shallow objects, then we don't know which refs have
2139 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2140 # do this with projects that don't have shallow objects, since it is less
2141 # efficient.
2142 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2143 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002144
Mike Frysinger4847e052020-02-22 00:07:35 -05002145 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002146 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002147 if not quiet and sys.stdout.isatty():
2148 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002149 if not self.worktree:
2150 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002151 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002152
Mike Frysingere57f1142019-03-18 21:27:54 -04002153 if force_sync:
2154 cmd.append('--force')
2155
David Pursehouse74cfd272015-10-14 10:50:15 +09002156 if prune:
2157 cmd.append('--prune')
2158
LaMont Jonesadaa1d82022-02-10 17:34:36 +00002159 cmd.append(f'--recurse-submodules={"on-demand" if submodules else "no"}')
Martin Kellye4e94d22017-03-21 16:05:12 -07002160
Kuang-che Wu6856f982019-11-25 12:37:55 +08002161 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002162 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002163 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002164 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002165 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002166 spec.append('tag')
2167 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002168
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302169 if self.manifest.IsMirror and not current_branch_only:
2170 branch = None
2171 else:
2172 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002173 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002174 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002175 # Shallow checkout of a specific commit, fetch from that commit and not
2176 # the heads only as the commit might be deeper in the history.
2177 spec.append(branch)
Xin Liaabf79d2021-04-29 01:50:38 -07002178 if self.upstream:
2179 spec.append(self.upstream)
Kuang-che Wu6856f982019-11-25 12:37:55 +08002180 else:
2181 if is_sha1:
2182 branch = self.upstream
2183 if branch is not None and branch.strip():
2184 if not branch.startswith('refs/'):
2185 branch = R_HEADS + branch
2186 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2187
2188 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2189 # whole repo.
2190 if self.manifest.IsMirror and not spec:
2191 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2192
2193 # If using depth then we should not get all the tags since they may
2194 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002195 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002196 cmd.append('--no-tags')
2197 else:
2198 cmd.append('--tags')
2199 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2200
Conley Owens80b87fe2014-05-09 17:13:44 -07002201 cmd.extend(spec)
2202
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002203 # At least one retry minimum due to git remote prune.
2204 retry_fetches = max(retry_fetches, 2)
2205 retry_cur_sleep = retry_sleep_initial_sec
2206 ok = prune_tried = False
2207 for try_n in range(retry_fetches):
Mike Frysinger67d6cdf2021-12-23 17:36:09 -05002208 gitcmd = GitCommand(
2209 self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects'),
2210 ssh_proxy=ssh_proxy,
2211 merge_output=True, capture_stdout=quiet or bool(output_redir))
Mike Frysinger7b586f22021-02-23 18:38:39 -05002212 if gitcmd.stdout and not quiet and output_redir:
2213 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002214 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002215 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002216 ok = True
2217 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002218
2219 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002220 elif (gitcmd.stdout and
2221 'error:' in gitcmd.stdout and
2222 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002223 # Fallthru to sleep+retry logic at the bottom.
2224 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002225
Mike Frysinger6823bc22021-04-15 02:06:28 -04002226 # Try to prune remote branches once in case there are conflicts.
2227 # For example, if the remote had refs/heads/upstream, but deleted that and
2228 # now has refs/heads/upstream/foo.
2229 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002230 'error:' in gitcmd.stdout and
2231 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002232 not prune_tried):
2233 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002234 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002235 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002236 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002237 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002238 break
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002239 print('retrying fetch after pruning remote branches', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002240 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002241 continue
Brian Harring14a66742012-09-28 20:21:57 -07002242 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002243 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2244 # in sha1 mode, we just tried sync'ing from the upstream field; it
2245 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002246 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002247 elif ret < 0:
2248 # Git died with a signal, exit immediately
2249 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002250
2251 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002252 if not verbose and gitcmd.stdout:
2253 print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002254 if try_n < retry_fetches - 1:
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002255 print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep),
2256 file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002257 time.sleep(retry_cur_sleep)
2258 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2259 MAXIMUM_RETRY_SLEEP_SEC)
2260 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2261 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002262
2263 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002264 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002265 if old_packed != '':
2266 _lwrite(packed_refs, old_packed)
2267 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002268 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002269 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002270
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002271 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002272 # We just synced the upstream given branch; verify we
2273 # got what we wanted, else trigger a second run of all
2274 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002275 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002276 # Sync the current branch only with depth set to None.
2277 # We always pass depth=None down to avoid infinite recursion.
2278 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002279 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002280 current_branch_only=current_branch_only and depth,
2281 initial=False, alt_dir=alt_dir,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002282 depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002283
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002284 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002285
Mike Frysingere50b6a72020-02-19 01:45:48 -05002286 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
LaMont Jonesd82be3e2022-04-05 19:30:46 +00002287 if initial and (self.manifest.manifestProject.depth or self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002288 return False
2289
2290 remote = self.GetRemote(self.remote.name)
2291 bundle_url = remote.url + '/clone.bundle'
2292 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002293 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2294 'persistent-http',
2295 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002296 return False
2297
2298 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2299 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2300
2301 exist_dst = os.path.exists(bundle_dst)
2302 exist_tmp = os.path.exists(bundle_tmp)
2303
2304 if not initial and not exist_dst and not exist_tmp:
2305 return False
2306
2307 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002308 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2309 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002310 if not exist_dst:
2311 return False
2312
2313 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002314 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002315 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002316 if not quiet and sys.stdout.isatty():
2317 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002318 if not self.worktree:
2319 cmd.append('--update-head-ok')
2320 cmd.append(bundle_dst)
2321 for f in remote.fetch:
2322 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002323 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002324
Mike Frysinger67d6cdf2021-12-23 17:36:09 -05002325 ok = GitCommand(
2326 self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects')).Wait() == 0
Mike Frysinger9d96f582021-09-28 11:27:24 -04002327 platform_utils.remove(bundle_dst, missing_ok=True)
2328 platform_utils.remove(bundle_tmp, missing_ok=True)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002329 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002330
Mike Frysingere50b6a72020-02-19 01:45:48 -05002331 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002332 platform_utils.remove(dstPath, missing_ok=True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002333
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002334 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002335 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002336 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002337 if os.path.exists(tmpPath):
2338 size = os.stat(tmpPath).st_size
2339 if size >= 1024:
2340 cmd += ['--continue-at', '%d' % (size,)]
2341 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002342 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002343 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002344 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002345 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002346 if proxy:
2347 cmd += ['--proxy', proxy]
2348 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2349 cmd += ['--proxy', os.environ['http_proxy']]
2350 if srcUrl.startswith('persistent-https'):
2351 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2352 elif srcUrl.startswith('persistent-http'):
2353 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002354 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002355
Dave Borowitz137d0132015-01-02 11:12:54 -08002356 if IsTrace():
2357 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002358 if verbose:
2359 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2360 stdout = None if verbose else subprocess.PIPE
2361 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002362 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002363 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002364 except OSError:
2365 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002366
Mike Frysingere50b6a72020-02-19 01:45:48 -05002367 (output, _) = proc.communicate()
2368 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002369
Dave Borowitz137d0132015-01-02 11:12:54 -08002370 if curlret == 22:
2371 # From curl man page:
2372 # 22: HTTP page not retrieved. The requested url was not found or
2373 # returned another error with the HTTP error code being 400 or above.
2374 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002375 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002376 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2377 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002378 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002379 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002380 elif curlret and not verbose and output:
2381 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002382
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002383 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002384 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002385 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002386 return True
2387 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002388 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002389 return False
2390 else:
2391 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002392
Kris Giesingc8d882a2014-12-23 13:02:32 -08002393 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002394 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002395 with open(path, 'rb') as f:
2396 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002397 return True
2398 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002399 if not quiet:
2400 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002401 return False
2402 except OSError:
2403 return False
2404
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002405 def _Checkout(self, rev, quiet=False):
2406 cmd = ['checkout']
2407 if quiet:
2408 cmd.append('-q')
2409 cmd.append(rev)
2410 cmd.append('--')
2411 if GitCommand(self, cmd).Wait() != 0:
2412 if self._allrefs:
2413 raise GitError('%s checkout %s ' % (self.name, rev))
2414
Mike Frysinger915fda12020-03-22 12:15:20 -04002415 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002416 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002417 if ffonly:
2418 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002419 if record_origin:
2420 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002421 cmd.append(rev)
2422 cmd.append('--')
2423 if GitCommand(self, cmd).Wait() != 0:
2424 if self._allrefs:
2425 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2426
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302427 def _LsRemote(self, refs):
2428 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302429 p = GitCommand(self, cmd, capture_stdout=True)
2430 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002431 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302432 return None
2433
Anthony King7bdac712014-07-16 12:56:40 +01002434 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002435 cmd = ['revert']
2436 cmd.append('--no-edit')
2437 cmd.append(rev)
2438 cmd.append('--')
2439 if GitCommand(self, cmd).Wait() != 0:
2440 if self._allrefs:
2441 raise GitError('%s revert %s ' % (self.name, rev))
2442
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002443 def _ResetHard(self, rev, quiet=True):
2444 cmd = ['reset', '--hard']
2445 if quiet:
2446 cmd.append('-q')
2447 cmd.append(rev)
2448 if GitCommand(self, cmd).Wait() != 0:
2449 raise GitError('%s reset --hard %s ' % (self.name, rev))
2450
Martin Kellye4e94d22017-03-21 16:05:12 -07002451 def _SyncSubmodules(self, quiet=True):
2452 cmd = ['submodule', 'update', '--init', '--recursive']
2453 if quiet:
2454 cmd.append('-q')
2455 if GitCommand(self, cmd).Wait() != 0:
LaMont Jones7b9b2512021-11-03 20:48:27 +00002456 raise GitError('%s submodule update --init --recursive ' % self.name)
Martin Kellye4e94d22017-03-21 16:05:12 -07002457
Anthony King7bdac712014-07-16 12:56:40 +01002458 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002459 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002460 if onto is not None:
2461 cmd.extend(['--onto', onto])
2462 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002463 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002464 raise GitError('%s rebase %s ' % (self.name, upstream))
2465
Pierre Tardy3d125942012-05-04 12:18:12 +02002466 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002467 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002468 if ffonly:
2469 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002470 if GitCommand(self, cmd).Wait() != 0:
2471 raise GitError('%s merge %s ' % (self.name, head))
2472
David Pursehousee8ace262020-02-13 12:41:15 +09002473 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002474 init_git_dir = not os.path.exists(self.gitdir)
2475 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002476 try:
2477 # Initialize the bare repository, which contains all of the objects.
2478 if init_obj_dir:
2479 os.makedirs(self.objdir)
2480 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002481
Mike Frysinger333c0a42021-11-15 12:39:00 -05002482 self._UpdateHooks(quiet=quiet)
2483
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002484 if self.use_git_worktrees:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002485 # Enable per-worktree config file support if possible. This is more a
2486 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002487 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002488 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002489
Kevin Degib1a07b82015-07-27 13:33:43 -06002490 # If we have a separate directory to hold refs, initialize it as well.
2491 if self.objdir != self.gitdir:
2492 if init_git_dir:
2493 os.makedirs(self.gitdir)
2494
2495 if init_obj_dir or init_git_dir:
Mike Frysingerc72bd842021-11-14 03:58:00 -05002496 self._ReferenceGitDir(self.objdir, self.gitdir, copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002497 try:
Mike Frysingerc72bd842021-11-14 03:58:00 -05002498 self._CheckDirReference(self.objdir, self.gitdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002499 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002500 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002501 print("Retrying clone after deleting %s" %
2502 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002503 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002504 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2505 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002506 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002507 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002508 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2509 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002510 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002511 raise e
2512 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002513
Kevin Degi384b3c52014-10-16 16:02:58 -06002514 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002515 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +00002516 ref_dir = mp.reference or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002517
LaMont Jonescc879a92021-11-18 22:40:18 +00002518 def _expanded_ref_dirs():
2519 """Iterate through the possible git reference directory paths."""
2520 name = self.name + '.git'
2521 yield mirror_git or os.path.join(ref_dir, name)
2522 for prefix in '', self.remote.name:
2523 yield os.path.join(ref_dir, '.repo', 'project-objects', prefix, name)
2524 yield os.path.join(ref_dir, '.repo', 'worktrees', prefix, name)
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002525
LaMont Jonescc879a92021-11-18 22:40:18 +00002526 if ref_dir or mirror_git:
2527 found_ref_dir = None
2528 for path in _expanded_ref_dirs():
2529 if os.path.exists(path):
2530 found_ref_dir = path
2531 break
2532 ref_dir = found_ref_dir
Shawn O. Pearce88443382010-10-08 10:02:09 +02002533
Kevin Degib1a07b82015-07-27 13:33:43 -06002534 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002535 if not os.path.isabs(ref_dir):
2536 # The alternate directory is relative to the object database.
2537 ref_dir = os.path.relpath(ref_dir,
2538 os.path.join(self.objdir, 'objects'))
Mike Frysinger152032c2021-12-20 21:17:43 -05002539 _lwrite(os.path.join(self.objdir, 'objects/info/alternates'),
Kevin Degib1a07b82015-07-27 13:33:43 -06002540 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002541
Kevin Degib1a07b82015-07-27 13:33:43 -06002542 m = self.manifest.manifestProject.config
2543 for key in ['user.name', 'user.email']:
2544 if m.Has(key, include_defaults=False):
2545 self.config.SetString(key, m.GetString(key))
XD Trol630876f2022-01-17 23:29:04 +08002546 if not self.manifest.EnableGitLfs:
2547 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
2548 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002549 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002550 except Exception:
2551 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002552 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002553 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002554 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002555 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002556
David Pursehousee8ace262020-02-13 12:41:15 +09002557 def _UpdateHooks(self, quiet=False):
Mike Frysinger333c0a42021-11-15 12:39:00 -05002558 if os.path.exists(self.objdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002559 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002560
David Pursehousee8ace262020-02-13 12:41:15 +09002561 def _InitHooks(self, quiet=False):
Mike Frysinger333c0a42021-11-15 12:39:00 -05002562 hooks = platform_utils.realpath(os.path.join(self.objdir, 'hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002563 if not os.path.exists(hooks):
2564 os.makedirs(hooks)
Mike Frysinger98bb7652021-12-20 21:15:59 -05002565
2566 # Delete sample hooks. They're noise.
2567 for hook in glob.glob(os.path.join(hooks, '*.sample')):
Peter Kjellerstedtb5505012022-01-21 23:09:19 +01002568 try:
2569 platform_utils.remove(hook, missing_ok=True)
2570 except PermissionError:
2571 pass
Mike Frysinger98bb7652021-12-20 21:15:59 -05002572
Jonathan Nieder93719792015-03-17 11:29:58 -07002573 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002574 name = os.path.basename(stock_hook)
2575
Victor Boivie65e0f352011-04-18 11:23:29 +02002576 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002577 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002578 # Don't install a Gerrit Code Review hook if this
2579 # project does not appear to use it for reviews.
2580 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002581 # Since the manifest project is one of those, but also
2582 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002583 continue
2584
2585 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002586 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002587 continue
2588 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002589 # If the files are the same, we'll leave it alone. We create symlinks
2590 # below by default but fallback to hardlinks if the OS blocks them.
2591 # So if we're here, it's probably because we made a hardlink below.
2592 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002593 if not quiet:
2594 _warn("%s: Not replacing locally modified %s hook",
2595 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002596 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002597 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002598 platform_utils.symlink(
2599 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002600 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002601 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002602 try:
2603 os.link(stock_hook, dst)
2604 except OSError:
2605 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002606 else:
2607 raise
2608
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002609 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002610 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002611 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002612 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002613 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002614 remote.review = self.remote.review
2615 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002616
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002617 if self.worktree:
2618 remote.ResetFetch(mirror=False)
2619 else:
2620 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002621 remote.Save()
2622
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002623 def _InitMRef(self):
2624 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002625 if self.use_git_worktrees:
Mike Frysinger29626b42021-05-01 09:37:13 -04002626 # Set up the m/ space to point to the worktree-specific ref space.
2627 # We'll update the worktree-specific ref space on each checkout.
2628 ref = R_M + self.manifest.branch
2629 if not self.bare_ref.symref(ref):
2630 self.bare_git.symbolic_ref(
2631 '-m', 'redirecting to worktree scope',
2632 ref, R_WORKTREE_M + self.manifest.branch)
2633
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002634 # We can't update this ref with git worktrees until it exists.
2635 # We'll wait until the initial checkout to set it.
2636 if not os.path.exists(self.worktree):
2637 return
2638
2639 base = R_WORKTREE_M
2640 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002641
2642 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002643 else:
2644 base = R_M
2645 active_git = self.bare_git
2646
2647 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002648
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002649 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002650 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002651
Remy Böhmer1469c282020-12-15 18:49:02 +01002652 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002653 cur = self.bare_ref.symref(ref)
2654
2655 if self.revisionId:
2656 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2657 msg = 'manifest set to %s' % self.revisionId
2658 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002659 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002660 else:
2661 remote = self.GetRemote(self.remote.name)
2662 dst = remote.ToLocal(self.revisionExpr)
2663 if cur != dst:
2664 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002665 if detach:
2666 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2667 else:
2668 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002669
Mike Frysingerc72bd842021-11-14 03:58:00 -05002670 def _CheckDirReference(self, srcdir, destdir):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002671 # Git worktrees don't use symlinks to share at all.
2672 if self.use_git_worktrees:
2673 return
2674
Mike Frysingerd33dce02021-12-20 18:16:33 -05002675 for name in self.shareable_dirs:
Mike Frysingered4f2112020-02-11 23:06:29 -05002676 # Try to self-heal a bit in simple cases.
2677 dst_path = os.path.join(destdir, name)
2678 src_path = os.path.join(srcdir, name)
2679
Mike Frysingered4f2112020-02-11 23:06:29 -05002680 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002681 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002682 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002683 # Fail if the links are pointing to the wrong place
2684 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002685 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002686 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002687 'work tree. If you\'re comfortable with the '
2688 'possibility of losing the work tree\'s git metadata,'
2689 ' use `repo sync --force-sync {0}` to '
2690 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002691
Mike Frysingerc72bd842021-11-14 03:58:00 -05002692 def _ReferenceGitDir(self, gitdir, dotgit, copy_all):
David James8d201162013-10-11 17:03:19 -07002693 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2694
2695 Args:
2696 gitdir: The bare git repository. Must already be initialized.
2697 dotgit: The repository you would like to initialize.
David James8d201162013-10-11 17:03:19 -07002698 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2699 This saves you the effort of initializing |dotgit| yourself.
2700 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002701 symlink_dirs = self.shareable_dirs[:]
Mike Frysingerd33dce02021-12-20 18:16:33 -05002702 to_symlink = symlink_dirs
David James8d201162013-10-11 17:03:19 -07002703
2704 to_copy = []
2705 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002706 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002707
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002708 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002709 for name in set(to_copy).union(to_symlink):
2710 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002711 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002712 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002713
Kevin Degi384b3c52014-10-16 16:02:58 -06002714 if os.path.lexists(dst):
2715 continue
David James8d201162013-10-11 17:03:19 -07002716
2717 # If the source dir doesn't exist, create an empty dir.
2718 if name in symlink_dirs and not os.path.lexists(src):
2719 os.makedirs(src)
2720
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002721 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002722 platform_utils.symlink(
2723 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002724 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002725 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002726 shutil.copytree(src, dst)
2727 elif os.path.isfile(src):
2728 shutil.copy(src, dst)
2729
David James8d201162013-10-11 17:03:19 -07002730 except OSError as e:
2731 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002732 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002733 else:
2734 raise
2735
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002736 def _InitGitWorktree(self):
2737 """Init the project using git worktrees."""
2738 self.bare_git.worktree('prune')
2739 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2740 self.worktree, self.GetRevisionId())
2741
2742 # Rewrite the internal state files to use relative paths between the
2743 # checkouts & worktrees.
2744 dotgit = os.path.join(self.worktree, '.git')
2745 with open(dotgit, 'r') as fp:
2746 # Figure out the checkout->worktree path.
2747 setting = fp.read()
2748 assert setting.startswith('gitdir:')
2749 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002750 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2751 # of file permissions. Delete it and recreate it from scratch to avoid.
2752 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002753 # Use relative path from checkout->worktree & maintain Unix line endings
2754 # on all OS's to match git behavior.
2755 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002756 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2757 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002758 # Use relative path from worktree->checkout & maintain Unix line endings
2759 # on all OS's to match git behavior.
2760 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002761 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2762
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002763 self._InitMRef()
2764
Martin Kellye4e94d22017-03-21 16:05:12 -07002765 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002766 """Setup the worktree .git path.
2767
2768 This is the user-visible path like src/foo/.git/.
2769
2770 With non-git-worktrees, this will be a symlink to the .repo/projects/ path.
2771 With git-worktrees, this will be a .git file using "gitdir: ..." syntax.
2772
2773 Older checkouts had .git/ directories. If we see that, migrate it.
2774
2775 This also handles changes in the manifest. Maybe this project was backed
2776 by "foo/bar" on the server, but now it's "new/foo/bar". We have to update
2777 the path we point to under .repo/projects/ to match.
2778 """
2779 dotgit = os.path.join(self.worktree, '.git')
2780
2781 # If using an old layout style (a directory), migrate it.
2782 if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
2783 self._MigrateOldWorkTreeGitDir(dotgit)
2784
2785 init_dotgit = not os.path.exists(dotgit)
2786 if self.use_git_worktrees:
2787 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002788 self._InitGitWorktree()
2789 self._CopyAndLinkFiles()
Mike Frysingerf4545122019-11-11 04:34:16 -05002790 else:
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002791 if not init_dotgit:
2792 # See if the project has changed.
2793 if platform_utils.realpath(self.gitdir) != platform_utils.realpath(dotgit):
2794 platform_utils.remove(dotgit)
Mike Frysingerf4545122019-11-11 04:34:16 -05002795
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002796 if init_dotgit or not os.path.exists(dotgit):
2797 os.makedirs(self.worktree, exist_ok=True)
2798 platform_utils.symlink(os.path.relpath(self.gitdir, self.worktree), dotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002799
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002800 if init_dotgit:
2801 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002802
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002803 # Finish checking out the worktree.
2804 cmd = ['read-tree', '--reset', '-u', '-v', HEAD]
2805 if GitCommand(self, cmd).Wait() != 0:
2806 raise GitError('Cannot initialize work tree for ' + self.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002808 if submodules:
2809 self._SyncSubmodules(quiet=True)
2810 self._CopyAndLinkFiles()
Victor Boivie0960b5b2010-11-26 13:42:13 +01002811
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002812 @classmethod
2813 def _MigrateOldWorkTreeGitDir(cls, dotgit):
2814 """Migrate the old worktree .git/ dir style to a symlink.
2815
2816 This logic specifically only uses state from |dotgit| to figure out where to
2817 move content and not |self|. This way if the backing project also changed
2818 places, we only do the .git/ dir to .git symlink migration here. The path
2819 updates will happen independently.
2820 """
2821 # Figure out where in .repo/projects/ it's pointing to.
2822 if not os.path.islink(os.path.join(dotgit, 'refs')):
2823 raise GitError(f'{dotgit}: unsupported checkout state')
2824 gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, 'refs')))
2825
2826 # Remove known symlink paths that exist in .repo/projects/.
2827 KNOWN_LINKS = {
2828 'config', 'description', 'hooks', 'info', 'logs', 'objects',
2829 'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
2830 }
2831 # Paths that we know will be in both, but are safe to clobber in .repo/projects/.
2832 SAFE_TO_CLOBBER = {
Mike Frysinger8e912482022-01-26 04:03:34 -05002833 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'gc.log', 'gitk.cache', 'index',
2834 'ORIG_HEAD',
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002835 }
2836
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002837 # First see if we'd succeed before starting the migration.
2838 unknown_paths = []
2839 for name in platform_utils.listdir(dotgit):
2840 # Ignore all temporary/backup names. These are common with vim & emacs.
2841 if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
2842 continue
2843
2844 dotgit_path = os.path.join(dotgit, name)
2845 if name in KNOWN_LINKS:
2846 if not platform_utils.islink(dotgit_path):
2847 unknown_paths.append(f'{dotgit_path}: should be a symlink')
2848 else:
2849 gitdir_path = os.path.join(gitdir, name)
2850 if name not in SAFE_TO_CLOBBER and os.path.exists(gitdir_path):
2851 unknown_paths.append(f'{dotgit_path}: unknown file; please file a bug')
2852 if unknown_paths:
2853 raise GitError('Aborting migration: ' + '\n'.join(unknown_paths))
2854
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002855 # Now walk the paths and sync the .git/ to .repo/projects/.
2856 for name in platform_utils.listdir(dotgit):
2857 dotgit_path = os.path.join(dotgit, name)
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002858
2859 # Ignore all temporary/backup names. These are common with vim & emacs.
2860 if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
2861 platform_utils.remove(dotgit_path)
2862 elif name in KNOWN_LINKS:
2863 platform_utils.remove(dotgit_path)
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002864 else:
2865 gitdir_path = os.path.join(gitdir, name)
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002866 platform_utils.remove(gitdir_path, missing_ok=True)
2867 platform_utils.rename(dotgit_path, gitdir_path)
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002868
2869 # Now that the dir should be empty, clear it out, and symlink it over.
2870 platform_utils.rmdir(dotgit)
2871 platform_utils.symlink(os.path.relpath(gitdir, os.path.dirname(dotgit)), dotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002872
Renaud Paquay788e9622017-01-27 11:41:12 -08002873 def _get_symlink_error_message(self):
2874 if platform_utils.isWindows():
2875 return ('Unable to create symbolic link. Please re-run the command as '
2876 'Administrator, or see '
2877 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2878 'for other options.')
2879 return 'filesystem must support symlinks'
2880
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002881 def _revlist(self, *args, **kw):
2882 a = []
2883 a.extend(args)
2884 a.append('--')
2885 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002886
2887 @property
2888 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002889 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002890
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002891 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002892 """Get logs between two revisions of this project."""
2893 comp = '..'
2894 if rev1:
2895 revs = [rev1]
2896 if rev2:
2897 revs.extend([comp, rev2])
2898 cmd = ['log', ''.join(revs)]
2899 out = DiffColoring(self.config)
2900 if out.is_on and color:
2901 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002902 if pretty_format is not None:
2903 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002904 if oneline:
2905 cmd.append('--oneline')
2906
2907 try:
2908 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2909 if log.Wait() == 0:
2910 return log.stdout
2911 except GitError:
2912 # worktree may not exist if groups changed for example. In that case,
2913 # try in gitdir instead.
2914 if not os.path.exists(self.worktree):
2915 return self.bare_git.log(*cmd[1:])
2916 else:
2917 raise
2918 return None
2919
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002920 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2921 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002922 """Get the list of logs from this revision to given revisionId"""
2923 logs = {}
2924 selfId = self.GetRevisionId(self._allrefs)
2925 toId = toProject.GetRevisionId(toProject._allrefs)
2926
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002927 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2928 pretty_format=pretty_format)
2929 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2930 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002931 return logs
2932
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002933 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002934
David James8d201162013-10-11 17:03:19 -07002935 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002936 self._project = project
2937 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002938 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002939
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002940 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2941 def __getstate__(self):
2942 return (self._project, self._bare, self._gitdir)
2943
2944 def __setstate__(self, state):
2945 self._project, self._bare, self._gitdir = state
2946
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002947 def LsOthers(self):
2948 p = GitCommand(self._project,
2949 ['ls-files',
2950 '-z',
2951 '--others',
2952 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002953 bare=False,
David James8d201162013-10-11 17:03:19 -07002954 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002955 capture_stdout=True,
2956 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002957 if p.Wait() == 0:
2958 out = p.stdout
2959 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002960 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002961 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002962 return []
2963
2964 def DiffZ(self, name, *args):
2965 cmd = [name]
2966 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002967 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002968 cmd.extend(args)
2969 p = GitCommand(self._project,
2970 cmd,
David James8d201162013-10-11 17:03:19 -07002971 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002972 bare=False,
2973 capture_stdout=True,
2974 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002975 p.Wait()
2976 r = {}
2977 out = p.stdout
2978 if out:
2979 out = iter(out[:-1].split('\0'))
2980 while out:
2981 try:
2982 info = next(out)
2983 path = next(out)
2984 except StopIteration:
2985 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002986
Mike Frysinger84230002021-02-16 17:08:35 -05002987 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002988
Mike Frysinger84230002021-02-16 17:08:35 -05002989 def __init__(self, path, omode, nmode, oid, nid, state):
2990 self.path = path
2991 self.src_path = None
2992 self.old_mode = omode
2993 self.new_mode = nmode
2994 self.old_id = oid
2995 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002996
Mike Frysinger84230002021-02-16 17:08:35 -05002997 if len(state) == 1:
2998 self.status = state
2999 self.level = None
3000 else:
3001 self.status = state[:1]
3002 self.level = state[1:]
3003 while self.level.startswith('0'):
3004 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003005
Mike Frysinger84230002021-02-16 17:08:35 -05003006 info = info[1:].split(' ')
3007 info = _Info(path, *info)
3008 if info.status in ('R', 'C'):
3009 info.src_path = info.path
3010 info.path = next(out)
3011 r[info.path] = info
3012 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003013
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003014 def GetDotgitPath(self, subpath=None):
3015 """Return the full path to the .git dir.
3016
3017 As a convenience, append |subpath| if provided.
3018 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003019 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003020 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003021 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003022 dotgit = os.path.join(self._project.worktree, '.git')
3023 if os.path.isfile(dotgit):
3024 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
3025 with open(dotgit) as fp:
3026 setting = fp.read()
3027 assert setting.startswith('gitdir:')
3028 gitdir = setting.split(':', 1)[1].strip()
3029 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
3030
3031 return dotgit if subpath is None else os.path.join(dotgit, subpath)
3032
3033 def GetHead(self):
3034 """Return the ref that HEAD points to."""
3035 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08003036 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05003037 with open(path) as fd:
3038 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04003039 except IOError as e:
3040 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07003041 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303042 line = line.decode()
3043 except AttributeError:
3044 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003045 if line.startswith('ref: '):
3046 return line[5:-1]
3047 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003048
3049 def SetHead(self, ref, message=None):
3050 cmdv = []
3051 if message is not None:
3052 cmdv.extend(['-m', message])
3053 cmdv.append(HEAD)
3054 cmdv.append(ref)
3055 self.symbolic_ref(*cmdv)
3056
3057 def DetachHead(self, new, message=None):
3058 cmdv = ['--no-deref']
3059 if message is not None:
3060 cmdv.extend(['-m', message])
3061 cmdv.append(HEAD)
3062 cmdv.append(new)
3063 self.update_ref(*cmdv)
3064
3065 def UpdateRef(self, name, new, old=None,
3066 message=None,
3067 detach=False):
3068 cmdv = []
3069 if message is not None:
3070 cmdv.extend(['-m', message])
3071 if detach:
3072 cmdv.append('--no-deref')
3073 cmdv.append(name)
3074 cmdv.append(new)
3075 if old is not None:
3076 cmdv.append(old)
3077 self.update_ref(*cmdv)
3078
3079 def DeleteRef(self, name, old=None):
3080 if not old:
3081 old = self.rev_parse(name)
3082 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003083 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003084
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003085 def rev_list(self, *args, **kw):
3086 if 'format' in kw:
3087 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3088 else:
3089 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003090 cmdv.extend(args)
3091 p = GitCommand(self._project,
3092 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003093 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003094 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003095 capture_stdout=True,
3096 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003097 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003098 raise GitError('%s rev-list %s: %s' %
3099 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003100 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003101
3102 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003103 """Allow arbitrary git commands using pythonic syntax.
3104
3105 This allows you to do things like:
3106 git_obj.rev_parse('HEAD')
3107
3108 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3109 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003110 Any other positional arguments will be passed to the git command, and the
3111 following keyword arguments are supported:
3112 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003113
3114 Args:
3115 name: The name of the git command to call. Any '_' characters will
3116 be replaced with '-'.
3117
3118 Returns:
3119 A callable object that will try to call git with the named command.
3120 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003121 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003122
Dave Borowitz091f8932012-10-23 17:01:04 -07003123 def runner(*args, **kwargs):
3124 cmdv = []
3125 config = kwargs.pop('config', None)
3126 for k in kwargs:
3127 raise TypeError('%s() got an unexpected keyword argument %r'
3128 % (name, k))
3129 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303130 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003131 cmdv.append('-c')
3132 cmdv.append('%s=%s' % (k, v))
3133 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003134 cmdv.extend(args)
3135 p = GitCommand(self._project,
3136 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003137 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003138 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003139 capture_stdout=True,
3140 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003141 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003142 raise GitError('%s %s: %s' %
3143 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003144 r = p.stdout
3145 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3146 return r[:-1]
3147 return r
3148 return runner
3149
3150
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003151class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003152
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003153 def __str__(self):
3154 return 'prior sync failed; rebase still in progress'
3155
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003156
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003157class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003158
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003159 def __str__(self):
3160 return 'contains uncommitted changes'
3161
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003162
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003163class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003164
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003165 def __init__(self, project, text):
3166 self.project = project
3167 self.text = text
3168
3169 def Print(self, syncbuf):
3170 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3171 syncbuf.out.nl()
3172
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003173
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003174class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003175
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003176 def __init__(self, project, why):
3177 self.project = project
3178 self.why = why
3179
3180 def Print(self, syncbuf):
3181 syncbuf.out.fail('error: %s/: %s',
3182 self.project.relpath,
3183 str(self.why))
3184 syncbuf.out.nl()
3185
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003186
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003187class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003188
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003189 def __init__(self, project, action):
3190 self.project = project
3191 self.action = action
3192
3193 def Run(self, syncbuf):
3194 out = syncbuf.out
3195 out.project('project %s/', self.project.relpath)
3196 out.nl()
3197 try:
3198 self.action()
3199 out.nl()
3200 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003201 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003202 out.nl()
3203 return False
3204
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003205
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003206class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003207
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003208 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003209 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003210 self.project = self.printer('header', attr='bold')
3211 self.info = self.printer('info')
3212 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003213
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003214
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003215class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003216
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003217 def __init__(self, config, detach_head=False):
3218 self._messages = []
3219 self._failures = []
3220 self._later_queue1 = []
3221 self._later_queue2 = []
3222
3223 self.out = _SyncColoring(config)
3224 self.out.redirect(sys.stderr)
3225
3226 self.detach_head = detach_head
3227 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003228 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003229
3230 def info(self, project, fmt, *args):
3231 self._messages.append(_InfoMessage(project, fmt % args))
3232
3233 def fail(self, project, err=None):
3234 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003235 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003236
3237 def later1(self, project, what):
3238 self._later_queue1.append(_Later(project, what))
3239
3240 def later2(self, project, what):
3241 self._later_queue2.append(_Later(project, what))
3242
3243 def Finish(self):
3244 self._PrintMessages()
3245 self._RunLater()
3246 self._PrintMessages()
3247 return self.clean
3248
David Rileye0684ad2017-04-05 00:02:59 -07003249 def Recently(self):
3250 recent_clean = self.recent_clean
3251 self.recent_clean = True
3252 return recent_clean
3253
3254 def _MarkUnclean(self):
3255 self.clean = False
3256 self.recent_clean = False
3257
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003258 def _RunLater(self):
3259 for q in ['_later_queue1', '_later_queue2']:
3260 if not self._RunQueue(q):
3261 return
3262
3263 def _RunQueue(self, queue):
3264 for m in getattr(self, queue):
3265 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003266 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003267 return False
3268 setattr(self, queue, [])
3269 return True
3270
3271 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003272 if self._messages or self._failures:
3273 if os.isatty(2):
3274 self.out.write(progress.CSI_ERASE_LINE)
3275 self.out.write('\r')
3276
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003277 for m in self._messages:
3278 m.Print(self)
3279 for m in self._failures:
3280 m.Print(self)
3281
3282 self._messages = []
3283 self._failures = []
3284
3285
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003286class MetaProject(Project):
LaMont Jones9b72cf22022-03-29 21:54:22 +00003287 """A special project housed under .repo."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003288
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003289 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003290 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003291 manifest=manifest,
3292 name=name,
3293 gitdir=gitdir,
3294 objdir=gitdir,
3295 worktree=worktree,
3296 remote=RemoteSpec('origin'),
3297 relpath='.repo/%s' % name,
3298 revisionExpr='refs/heads/master',
3299 revisionId=None,
3300 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003301
3302 def PreSync(self):
3303 if self.Exists:
3304 cb = self.CurrentBranch
3305 if cb:
3306 base = self.GetBranch(cb).merge
3307 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003308 self.revisionExpr = base
3309 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003310
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003311 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003312 def HasChanges(self):
LaMont Jones9b72cf22022-03-29 21:54:22 +00003313 """Has the remote received new commits not yet checked out?"""
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003314 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003315 return False
3316
David Pursehouse8a68ff92012-09-24 12:15:13 +09003317 all_refs = self.bare_ref.all
3318 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003319 head = self.work_git.GetHead()
3320 if head.startswith(R_HEADS):
3321 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003322 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003323 except KeyError:
3324 head = None
3325
3326 if revid == head:
3327 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003328 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003329 return True
3330 return False
LaMont Jones9b72cf22022-03-29 21:54:22 +00003331
3332
3333class RepoProject(MetaProject):
3334 """The MetaProject for repo itself."""
3335
3336 @property
3337 def LastFetch(self):
3338 try:
3339 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3340 return os.path.getmtime(fh)
3341 except OSError:
3342 return 0
3343
3344class ManifestProject(MetaProject):
3345 """The MetaProject for manifests."""
3346
3347 def MetaBranchSwitch(self, submodules=False):
3348 """Prepare for manifest branch switch."""
3349
3350 # detach and delete manifest branch, allowing a new
3351 # branch to take over
3352 syncbuf = SyncBuffer(self.config, detach_head=True)
3353 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3354 syncbuf.Finish()
3355
3356 return GitCommand(self,
3357 ['update-ref', '-d', 'refs/heads/default'],
3358 capture_stdout=True,
3359 capture_stderr=True).Wait() == 0
LaMont Jones9b03f152022-03-29 23:01:18 +00003360
3361 @property
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003362 def standalone_manifest_url(self):
3363 """The URL of the standalone manifest, or None."""
LaMont Jones55ee3042022-04-06 17:10:21 +00003364 return self.config.GetString('manifest.standalone')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003365
3366 @property
3367 def manifest_groups(self):
3368 """The manifest groups string."""
3369 return self.config.GetString('manifest.groups')
3370
3371 @property
3372 def reference(self):
3373 """The --reference for this manifest."""
3374 self.config.GetString('repo.reference')
3375
3376 @property
3377 def dissociate(self):
3378 """Whether to dissociate."""
3379 self.config.GetBoolean('repo.dissociate')
3380
3381 @property
3382 def archive(self):
3383 """Whether we use archive."""
3384 self.config.GetBoolean('repo.archive')
3385
3386 @property
3387 def mirror(self):
3388 """Whether we use mirror."""
3389 self.config.GetBoolean('repo.mirror')
3390
3391 @property
3392 def use_worktree(self):
3393 """Whether we use worktree."""
3394 self.config.GetBoolean('repo.worktree')
3395
3396 @property
3397 def clone_bundle(self):
3398 """Whether we use clone_bundle."""
3399 self.config.GetBoolean('repo.clonebundle')
3400
3401 @property
3402 def submodules(self):
3403 """Whether we use submodules."""
3404 self.config.GetBoolean('repo.submodules')
3405
3406 @property
3407 def git_lfs(self):
3408 """Whether we use git_lfs."""
3409 self.config.GetBoolean('repo.git-lfs')
3410
3411 @property
3412 def use_superproject(self):
3413 """Whether we use superproject."""
3414 self.config.GetBoolean('repo.superproject')
3415
3416 @property
3417 def partial_clone(self):
3418 """Whether this is a partial clone."""
3419 self.config.GetBoolean('repo.partialclone')
3420
3421 @property
3422 def depth(self):
3423 """Partial clone depth."""
3424 self.config.GetString('repo.depth')
3425
3426 @property
3427 def clone_filter(self):
3428 """The clone filter."""
3429 self.config.GetString('repo.clonefilter')
3430
3431 @property
3432 def partial_clone_exclude(self):
3433 """Partial clone exclude string"""
3434 self.config.GetBoolean('repo.partialcloneexclude')
3435
3436 @property
LaMont Jones9b03f152022-03-29 23:01:18 +00003437 def _platform_name(self):
3438 """Return the name of the platform."""
3439 return platform.system().lower()
3440
3441 def Sync(self, _kwargs_only=(), manifest_url='', manifest_branch=None,
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003442 standalone_manifest=False, groups='', mirror=False, reference='',
3443 dissociate=False, worktree=False, submodules=False, archive=False,
3444 partial_clone=None, depth=None, clone_filter='blob:none',
LaMont Jones9b03f152022-03-29 23:01:18 +00003445 partial_clone_exclude=None, clone_bundle=None, git_lfs=None,
3446 use_superproject=None, verbose=False, current_branch_only=False,
LaMont Jones55ee3042022-04-06 17:10:21 +00003447 git_event_log=None, platform='', manifest_name='default.xml',
3448 tags='', this_manifest_only=False, outer_manifest=True):
LaMont Jones9b03f152022-03-29 23:01:18 +00003449 """Sync the manifest and all submanifests.
3450
3451 Args:
3452 manifest_url: a string, the URL of the manifest project.
3453 manifest_branch: a string, the manifest branch to use.
3454 standalone_manifest: a boolean, whether to store the manifest as a static
3455 file.
3456 groups: a string, restricts the checkout to projects with the specified
3457 groups.
LaMont Jones9b03f152022-03-29 23:01:18 +00003458 mirror: a boolean, whether to create a mirror of the remote repository.
3459 reference: a string, location of a repo instance to use as a reference.
3460 dissociate: a boolean, whether to dissociate from reference mirrors after
3461 clone.
3462 worktree: a boolean, whether to use git-worktree to manage projects.
3463 submodules: a boolean, whether sync submodules associated with the
3464 manifest project.
3465 archive: a boolean, whether to checkout each project as an archive. See
3466 git-archive.
3467 partial_clone: a boolean, whether to perform a partial clone.
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003468 depth: an int, how deep of a shallow clone to create.
LaMont Jones9b03f152022-03-29 23:01:18 +00003469 clone_filter: a string, filter to use with partial_clone.
3470 partial_clone_exclude : a string, comma-delimeted list of project namess
3471 to exclude from partial clone.
3472 clone_bundle: a boolean, whether to enable /clone.bundle on HTTP/HTTPS.
3473 git_lfs: a boolean, whether to enable git LFS support.
3474 use_superproject: a boolean, whether to use the manifest superproject to
3475 sync projects.
3476 verbose: a boolean, whether to show all output, rather than only errors.
3477 current_branch_only: a boolean, whether to only fetch the current manifest
3478 branch from the server.
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003479 platform: a string, restrict the checkout to projects with the specified
3480 platform group.
LaMont Jones55ee3042022-04-06 17:10:21 +00003481 git_event_log: an EventLog, for git tracing.
LaMont Jones9b03f152022-03-29 23:01:18 +00003482 tags: a boolean, whether to fetch tags.,
LaMont Jones409407a2022-04-05 21:21:56 +00003483 manifest_name: a string, the name of the manifest file to use.
3484 this_manifest_only: a boolean, whether to only operate on the current sub
3485 manifest.
3486 outer_manifest: a boolean, whether to start at the outermost manifest.
LaMont Jones9b03f152022-03-29 23:01:18 +00003487
3488 Returns:
3489 a boolean, whether the sync was successful.
3490 """
3491 assert _kwargs_only == (), 'Sync only accepts keyword arguments.'
3492
LaMont Jonesa2ff20d2022-04-07 16:49:06 +00003493 groups = groups or 'default'
3494 platform = platform or 'auto'
LaMont Jones55ee3042022-04-06 17:10:21 +00003495 git_event_log = git_event_log or EventLog()
LaMont Jones409407a2022-04-05 21:21:56 +00003496 if outer_manifest and self.manifest.is_submanifest:
3497 # In a multi-manifest checkout, use the outer manifest unless we are told
3498 # not to.
3499 return self.client.outer_manifest.manifestProject.Sync(
3500 manifest_url=manifest_url,
3501 manifest_branch=manifest_branch,
3502 standalone_manifest=standalone_manifest,
3503 groups=groups,
3504 platform=platform,
3505 mirror=mirror,
3506 dissociate=dissociate,
3507 reference=reference,
3508 worktree=worktree,
3509 submodules=submodules,
3510 archive=archive,
3511 partial_clone=partial_clone,
3512 clone_filter=clone_filter,
3513 partial_clone_exclude=partial_clone_exclude,
3514 clone_bundle=clone_bundle,
3515 git_lfs=git_lfs,
3516 use_superproject=use_superproject,
3517 verbose=verbose,
3518 current_branch_only=current_branch_only,
3519 tags=tags,
3520 depth=depth,
LaMont Jones55ee3042022-04-06 17:10:21 +00003521 git_event_log=git_event_log,
LaMont Jones409407a2022-04-05 21:21:56 +00003522 manifest_name=manifest_name,
3523 this_manifest_only=this_manifest_only,
3524 outer_manifest=False)
3525
LaMont Jones9b03f152022-03-29 23:01:18 +00003526 # If repo has already been initialized, we take -u with the absence of
3527 # --standalone-manifest to mean "transition to a standard repo set up",
3528 # which necessitates starting fresh.
3529 # If --standalone-manifest is set, we always tear everything down and start
3530 # anew.
3531 if self.Exists:
3532 was_standalone_manifest = self.config.GetString('manifest.standalone')
3533 if was_standalone_manifest and not manifest_url:
3534 print('fatal: repo was initialized with a standlone manifest, '
3535 'cannot be re-initialized without --manifest-url/-u')
3536 return False
3537
3538 if standalone_manifest or (was_standalone_manifest and manifest_url):
3539 self.config.ClearCache()
3540 if self.gitdir and os.path.exists(self.gitdir):
3541 platform_utils.rmtree(self.gitdir)
3542 if self.worktree and os.path.exists(self.worktree):
3543 platform_utils.rmtree(self.worktree)
3544
3545 is_new = not self.Exists
3546 if is_new:
3547 if not manifest_url:
3548 print('fatal: manifest url is required.', file=sys.stderr)
3549 return False
3550
LaMont Jones409407a2022-04-05 21:21:56 +00003551 if verbose:
LaMont Jones9b03f152022-03-29 23:01:18 +00003552 print('Downloading manifest from %s' %
3553 (GitConfig.ForUser().UrlInsteadOf(manifest_url),),
3554 file=sys.stderr)
3555
3556 # The manifest project object doesn't keep track of the path on the
3557 # server where this git is located, so let's save that here.
3558 mirrored_manifest_git = None
3559 if reference:
3560 manifest_git_path = urllib.parse.urlparse(manifest_url).path[1:]
3561 mirrored_manifest_git = os.path.join(reference, manifest_git_path)
3562 if not mirrored_manifest_git.endswith(".git"):
3563 mirrored_manifest_git += ".git"
3564 if not os.path.exists(mirrored_manifest_git):
3565 mirrored_manifest_git = os.path.join(reference,
3566 '.repo/manifests.git')
3567
3568 self._InitGitDir(mirror_git=mirrored_manifest_git)
3569
3570 # If standalone_manifest is set, mark the project as "standalone" -- we'll
3571 # still do much of the manifests.git set up, but will avoid actual syncs to
3572 # a remote.
3573 if standalone_manifest:
3574 self.config.SetString('manifest.standalone', manifest_url)
3575 elif not manifest_url and not manifest_branch:
3576 # If -u is set and --standalone-manifest is not, then we're not in
3577 # standalone mode. Otherwise, use config to infer what we were in the last
3578 # init.
3579 standalone_manifest = bool(self.config.GetString('manifest.standalone'))
3580 if not standalone_manifest:
3581 self.config.SetString('manifest.standalone', None)
3582
3583 self._ConfigureDepth(depth)
3584
3585 # Set the remote URL before the remote branch as we might need it below.
3586 if manifest_url:
3587 r = self.GetRemote(self.remote.name)
3588 r.url = manifest_url
3589 r.ResetFetch()
3590 r.Save()
3591
3592 if not standalone_manifest:
3593 if manifest_branch:
3594 if manifest_branch == 'HEAD':
3595 manifest_branch = self.ResolveRemoteHead()
3596 if manifest_branch is None:
3597 print('fatal: unable to resolve HEAD', file=sys.stderr)
3598 return False
3599 self.revisionExpr = manifest_branch
3600 else:
3601 if is_new:
3602 default_branch = self.ResolveRemoteHead()
3603 if default_branch is None:
3604 # If the remote doesn't have HEAD configured, default to master.
3605 default_branch = 'refs/heads/master'
3606 self.revisionExpr = default_branch
3607 else:
3608 self.PreSync()
3609
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003610 groups = re.split(r'[,\s]+', groups or '')
LaMont Jones9b03f152022-03-29 23:01:18 +00003611 all_platforms = ['linux', 'darwin', 'windows']
3612 platformize = lambda x: 'platform-' + x
3613 if platform == 'auto':
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003614 if not mirror and not self.mirror:
LaMont Jones9b03f152022-03-29 23:01:18 +00003615 groups.append(platformize(self._platform_name))
3616 elif platform == 'all':
3617 groups.extend(map(platformize, all_platforms))
3618 elif platform in all_platforms:
3619 groups.append(platformize(platform))
3620 elif platform != 'none':
3621 print('fatal: invalid platform flag', file=sys.stderr)
3622 return False
3623
3624 groups = [x for x in groups if x]
3625 groupstr = ','.join(groups)
3626 if platform == 'auto' and groupstr == self.manifest.GetDefaultGroupsStr():
3627 groupstr = None
3628 self.config.SetString('manifest.groups', groupstr)
3629
3630 if reference:
3631 self.config.SetString('repo.reference', reference)
3632
3633 if dissociate:
3634 self.config.SetBoolean('repo.dissociate', dissociate)
3635
3636 if worktree:
3637 if mirror:
3638 print('fatal: --mirror and --worktree are incompatible',
3639 file=sys.stderr)
3640 return False
3641 if submodules:
3642 print('fatal: --submodules and --worktree are incompatible',
3643 file=sys.stderr)
3644 return False
3645 self.config.SetBoolean('repo.worktree', worktree)
3646 if is_new:
3647 self.use_git_worktrees = True
3648 print('warning: --worktree is experimental!', file=sys.stderr)
3649
3650 if archive:
3651 if is_new:
3652 self.config.SetBoolean('repo.archive', archive)
3653 else:
3654 print('fatal: --archive is only supported when initializing a new '
3655 'workspace.', file=sys.stderr)
3656 print('Either delete the .repo folder in this workspace, or initialize '
3657 'in another location.', file=sys.stderr)
3658 return False
3659
3660 if mirror:
3661 if is_new:
3662 self.config.SetBoolean('repo.mirror', mirror)
3663 else:
3664 print('fatal: --mirror is only supported when initializing a new '
3665 'workspace.', file=sys.stderr)
3666 print('Either delete the .repo folder in this workspace, or initialize '
3667 'in another location.', file=sys.stderr)
3668 return False
3669
3670 if partial_clone is not None:
3671 if mirror:
3672 print('fatal: --mirror and --partial-clone are mutually exclusive',
3673 file=sys.stderr)
3674 return False
3675 self.config.SetBoolean('repo.partialclone', partial_clone)
3676 if clone_filter:
3677 self.config.SetString('repo.clonefilter', clone_filter)
LaMont Jones55ee3042022-04-06 17:10:21 +00003678 elif self.partial_clone:
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003679 clone_filter = self.clone_filter
LaMont Jones9b03f152022-03-29 23:01:18 +00003680 else:
3681 clone_filter = None
3682
3683 if partial_clone_exclude is not None:
3684 self.config.SetString('repo.partialcloneexclude', partial_clone_exclude)
3685
3686 if clone_bundle is None:
3687 clone_bundle = False if partial_clone else True
3688 else:
3689 self.config.SetBoolean('repo.clonebundle', clone_bundle)
3690
3691 if submodules:
3692 self.config.SetBoolean('repo.submodules', submodules)
3693
3694 if git_lfs is not None:
3695 if git_lfs:
3696 git_require((2, 17, 0), fail=True, msg='Git LFS support')
3697
3698 self.config.SetBoolean('repo.git-lfs', git_lfs)
3699 if not is_new:
3700 print('warning: Changing --git-lfs settings will only affect new project checkouts.\n'
3701 ' Existing projects will require manual updates.\n', file=sys.stderr)
3702
3703 if use_superproject is not None:
3704 self.config.SetBoolean('repo.superproject', use_superproject)
3705
3706 if standalone_manifest:
3707 if is_new:
3708 manifest_name = 'default.xml'
3709 manifest_data = fetch.fetch_file(manifest_url, verbose=verbose)
3710 dest = os.path.join(self.worktree, manifest_name)
3711 os.makedirs(os.path.dirname(dest), exist_ok=True)
3712 with open(dest, 'wb') as f:
3713 f.write(manifest_data)
3714 return
3715
3716 if not self.Sync_NetworkHalf(is_new=is_new, quiet=not verbose, verbose=verbose,
3717 clone_bundle=clone_bundle,
3718 current_branch_only=current_branch_only,
3719 tags=tags, submodules=submodules,
3720 clone_filter=clone_filter,
3721 partial_clone_exclude=self.manifest.PartialCloneExclude):
3722 r = self.GetRemote(self.remote.name)
3723 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
3724
3725 # Better delete the manifest git dir if we created it; otherwise next
3726 # time (when user fixes problems) we won't go through the "is_new" logic.
3727 if is_new:
3728 platform_utils.rmtree(self.gitdir)
3729 return False
3730
3731 if manifest_branch:
3732 self.MetaBranchSwitch(submodules=submodules)
3733
3734 syncbuf = SyncBuffer(self.config)
3735 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3736 syncbuf.Finish()
3737
3738 if is_new or self.CurrentBranch is None:
3739 if not self.StartBranch('default'):
3740 print('fatal: cannot create default in manifest', file=sys.stderr)
3741 return False
3742
LaMont Jones409407a2022-04-05 21:21:56 +00003743 if not manifest_name:
3744 print('fatal: manifest name (-m) is required.', file=sys.stderr)
3745 return False
3746
3747 try:
3748 self.manifest.Link(manifest_name)
3749 except ManifestParseError as e:
3750 print("fatal: manifest '%s' not available" % manifest_name,
3751 file=sys.stderr)
3752 print('fatal: %s' % str(e), file=sys.stderr)
3753 return False
3754
LaMont Jones55ee3042022-04-06 17:10:21 +00003755 if not this_manifest_only:
3756 for submanifest in self.manifest.submanifests.values():
3757 spec = submanifest.ToSubmanifestSpec(root=self.manifest.outer_client)
3758 submanifest.repo_client.manifestProject.Sync(
3759 manifest_url=spec.manifestUrl,
3760 manifest_branch=spec.revision,
3761 standalone_manifest=standalone_manifest,
3762 groups=self.manifest_groups,
3763 platform=platform,
3764 mirror=mirror,
3765 dissociate=dissociate,
3766 reference=reference,
3767 worktree=worktree,
3768 submodules=submodules,
3769 archive=archive,
3770 partial_clone=partial_clone,
3771 clone_filter=clone_filter,
3772 partial_clone_exclude=partial_clone_exclude,
3773 clone_bundle=clone_bundle,
3774 git_lfs=git_lfs,
3775 use_superproject=use_superproject,
3776 verbose=verbose,
3777 current_branch_only=current_branch_only,
3778 tags=tags,
3779 depth=depth,
3780 git_event_log=git_event_log,
3781 manifest_name=spec.manifestName,
3782 this_manifest_only=False,
3783 outer_manifest=False,
3784 )
LaMont Jones409407a2022-04-05 21:21:56 +00003785
LaMont Jones55ee3042022-04-06 17:10:21 +00003786 # Lastly, clone the superproject(s).
LaMont Jonesa2ff20d2022-04-07 16:49:06 +00003787 if self.manifest.manifestProject.use_superproject:
3788 sync_result = Superproject(
3789 self.manifest, self.manifest.repodir, git_event_log, quiet=not verbose).Sync()
3790 if not sync_result.success:
3791 print('warning: git update of superproject for '
3792 f'{self.manifest.path_prefix} failed, repo sync will not use '
3793 'superproject to fetch source; while this error is not fatal, '
3794 'and you can continue to run repo sync, please run repo init '
3795 'with the --no-use-superproject option to stop seeing this '
3796 'warning', file=sys.stderr)
3797 if sync_result.fatal and use_superproject is not None:
3798 return False
LaMont Jones409407a2022-04-05 21:21:56 +00003799
LaMont Jones9b03f152022-03-29 23:01:18 +00003800 return True
3801
3802 def _ConfigureDepth(self, depth):
3803 """Configure the depth we'll sync down.
3804
3805 Args:
3806 depth: an int, how deep of a partial clone to create.
3807 """
3808 # Opt.depth will be non-None if user actually passed --depth to repo init.
3809 if depth is not None:
3810 if depth > 0:
3811 # Positive values will set the depth.
3812 depth = str(depth)
3813 else:
3814 # Negative numbers will clear the depth; passing None to SetString
3815 # will do that.
3816 depth = None
3817
3818 # We store the depth in the main manifest project.
3819 self.config.SetString('repo.depth', depth)