blob: b7ed6f336bd10e61993481d53c3c9be7b0f10df3 [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
LaMont Jones0de4fc32022-04-21 17:18:35 +000032import fetch
Dave Borowitzb42b4742012-10-31 12:27:27 -070033from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070034from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
35 ID_RE
LaMont Jones55ee3042022-04-06 17:10:21 +000036from git_trace2_event_log import EventLog
Remy Bohmer16c13282020-09-10 10:38:04 +020037from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040038from error import ManifestInvalidRevisionError, ManifestInvalidPathError
LaMont Jones409407a2022-04-05 21:21:56 +000039from error import NoManifestException, ManifestParseError
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070040import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040041import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040042from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050044from 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 -070045
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070046
George Engelbrecht9bc283e2020-04-02 12:36:09 -060047# Maximum sleep time allowed during retries.
48MAXIMUM_RETRY_SLEEP_SEC = 3600.0
49# +-10% random jitter is added to each Fetches retry sleep duration.
50RETRY_JITTER_PERCENT = 0.1
51
52
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070053def _lwrite(path, content):
54 lock = '%s.lock' % path
55
Remy Bohmer169b0212020-11-21 10:57:52 +010056 # Maintain Unix line endings on all OS's to match git behavior.
57 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070058 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059
60 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070061 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080063 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070064 raise
65
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070066
Shawn O. Pearce48244782009-04-16 08:25:57 -070067def _error(fmt, *args):
68 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070069 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070070
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
David Pursehousef33929d2015-08-24 14:39:14 +090072def _warn(fmt, *args):
73 msg = fmt % args
74 print('warn: %s' % msg, file=sys.stderr)
75
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070077def not_rev(r):
78 return '^' + r
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080081def sq(r):
82 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080083
David Pursehouse819827a2020-02-12 15:20:19 +090084
Jonathan Nieder93719792015-03-17 11:29:58 -070085_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070086
87
Jonathan Nieder93719792015-03-17 11:29:58 -070088def _ProjectHooks():
89 """List the hooks present in the 'hooks' directory.
90
91 These hooks are project hooks and are copied to the '.git/hooks' directory
92 of all subprojects.
93
94 This function caches the list of hooks (based on the contents of the
95 'repo/hooks' directory) on the first call.
96
97 Returns:
98 A list of absolute paths to all of the files in the hooks directory.
99 """
100 global _project_hook_list
101 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700102 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700103 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700104 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700105 return _project_hook_list
106
107
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700108class DownloadedChange(object):
109 _commit_cache = None
110
111 def __init__(self, project, base, change_id, ps_id, commit):
112 self.project = project
113 self.base = base
114 self.change_id = change_id
115 self.ps_id = ps_id
116 self.commit = commit
117
118 @property
119 def commits(self):
120 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700121 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
122 '--abbrev-commit',
123 '--pretty=oneline',
124 '--reverse',
125 '--date-order',
126 not_rev(self.base),
127 self.commit,
128 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700129 return self._commit_cache
130
131
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700132class ReviewableBranch(object):
133 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400134 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135
136 def __init__(self, project, branch, base):
137 self.project = project
138 self.branch = branch
139 self.base = base
140
141 @property
142 def name(self):
143 return self.branch.name
144
145 @property
146 def commits(self):
147 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400148 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
149 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
150 try:
151 self._commit_cache = self.project.bare_git.rev_list(*args)
152 except GitError:
153 # We weren't able to probe the commits for this branch. Was it tracking
154 # a branch that no longer exists? If so, return no commits. Otherwise,
155 # rethrow the error as we don't know what's going on.
156 if self.base_exists:
157 raise
158
159 self._commit_cache = []
160
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700161 return self._commit_cache
162
163 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800164 def unabbrev_commits(self):
165 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700166 for commit in self.project.bare_git.rev_list(not_rev(self.base),
167 R_HEADS + self.name,
168 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800169 r[commit[0:8]] = commit
170 return r
171
172 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700173 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700174 return self.project.bare_git.log('--pretty=format:%cd',
175 '-n', '1',
176 R_HEADS + self.name,
177 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178
Mike Frysinger6da17752019-09-11 18:43:17 -0400179 @property
180 def base_exists(self):
181 """Whether the branch we're tracking exists.
182
183 Normally it should, but sometimes branches we track can get deleted.
184 """
185 if self._base_exists is None:
186 try:
187 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
188 # If we're still here, the base branch exists.
189 self._base_exists = True
190 except GitError:
191 # If we failed to verify, the base branch doesn't exist.
192 self._base_exists = False
193
194 return self._base_exists
195
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700196 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500197 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700198 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500199 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500200 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200201 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700202 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200203 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200204 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800205 validate_certs=True,
206 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500207 self.project.UploadForReview(branch=self.name,
208 people=people,
209 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700210 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500211 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500212 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200213 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700214 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200215 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200216 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800217 validate_certs=validate_certs,
218 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700220 def GetPublishedRefs(self):
221 refs = {}
222 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700223 self.branch.remote.SshReviewUrl(self.project.UserEmail),
224 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700225 for line in output.split('\n'):
226 try:
227 (sha, ref) = line.split()
228 refs[sha] = ref
229 except ValueError:
230 pass
231
232 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700234
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500238 super().__init__(config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100239 self.project = self.printer('header', attr='bold')
240 self.branch = self.printer('header', attr='bold')
241 self.nobranch = self.printer('nobranch', fg='red')
242 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243
Anthony King7bdac712014-07-16 12:56:40 +0100244 self.added = self.printer('added', fg='green')
245 self.changed = self.printer('changed', fg='red')
246 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247
248
249class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700250
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500252 super().__init__(config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100253 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400254 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700256
Jack Neus6ea0cae2021-07-20 20:52:33 +0000257class Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700258
James W. Mills24c13082012-04-12 15:04:13 -0500259 def __init__(self, name, value, keep):
260 self.name = name
261 self.value = value
262 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263
Jack Neus6ea0cae2021-07-20 20:52:33 +0000264 def __eq__(self, other):
265 if not isinstance(other, Annotation):
266 return False
267 return self.__dict__ == other.__dict__
268
269 def __lt__(self, other):
270 # This exists just so that lists of Annotation objects can be sorted, for
271 # use in comparisons.
272 if not isinstance(other, Annotation):
273 raise ValueError('comparison is not between two Annotation objects')
274 if self.name == other.name:
275 if self.value == other.value:
276 return self.keep < other.keep
277 return self.value < other.value
278 return self.name < other.name
279
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700280
Mike Frysingere6a202f2019-08-02 15:57:57 -0400281def _SafeExpandPath(base, subpath, skipfinal=False):
282 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700283
Mike Frysingere6a202f2019-08-02 15:57:57 -0400284 We make sure no intermediate symlinks are traversed, and that the final path
285 is not a special file (e.g. not a socket or fifo).
286
287 NB: We rely on a number of paths already being filtered out while parsing the
288 manifest. See the validation logic in manifest_xml.py for more details.
289 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500290 # Split up the path by its components. We can't use os.path.sep exclusively
291 # as some platforms (like Windows) will convert / to \ and that bypasses all
292 # our constructed logic here. Especially since manifest authors only use
293 # / in their paths.
294 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
295 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400296 if skipfinal:
297 # Whether the caller handles the final component itself.
298 finalpart = components.pop()
299
300 path = base
301 for part in components:
302 if part in {'.', '..'}:
303 raise ManifestInvalidPathError(
304 '%s: "%s" not allowed in paths' % (subpath, part))
305
306 path = os.path.join(path, part)
307 if platform_utils.islink(path):
308 raise ManifestInvalidPathError(
309 '%s: traversing symlinks not allow' % (path,))
310
311 if os.path.exists(path):
312 if not os.path.isfile(path) and not platform_utils.isdir(path):
313 raise ManifestInvalidPathError(
314 '%s: only regular files & directories allowed' % (path,))
315
316 if skipfinal:
317 path = os.path.join(path, finalpart)
318
319 return path
320
321
322class _CopyFile(object):
323 """Container for <copyfile> manifest element."""
324
325 def __init__(self, git_worktree, src, topdir, dest):
326 """Register a <copyfile> request.
327
328 Args:
329 git_worktree: Absolute path to the git project checkout.
330 src: Relative path under |git_worktree| of file to read.
331 topdir: Absolute path to the top of the repo client checkout.
332 dest: Relative path under |topdir| of file to write.
333 """
334 self.git_worktree = git_worktree
335 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700336 self.src = src
337 self.dest = dest
338
339 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400340 src = _SafeExpandPath(self.git_worktree, self.src)
341 dest = _SafeExpandPath(self.topdir, self.dest)
342
343 if platform_utils.isdir(src):
344 raise ManifestInvalidPathError(
345 '%s: copying from directory not supported' % (self.src,))
346 if platform_utils.isdir(dest):
347 raise ManifestInvalidPathError(
348 '%s: copying to directory not allowed' % (self.dest,))
349
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700350 # copy file if it does not exist or is out of date
351 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
352 try:
353 # remove existing file first, since it might be read-only
354 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800355 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400356 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200357 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700358 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200359 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700360 shutil.copy(src, dest)
361 # make the file read-only
362 mode = os.stat(dest)[stat.ST_MODE]
363 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
364 os.chmod(dest, mode)
365 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700366 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700367
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700368
Anthony King7bdac712014-07-16 12:56:40 +0100369class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400370 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700371
Mike Frysingere6a202f2019-08-02 15:57:57 -0400372 def __init__(self, git_worktree, src, topdir, dest):
373 """Register a <linkfile> request.
374
375 Args:
376 git_worktree: Absolute path to the git project checkout.
377 src: Target of symlink relative to path under |git_worktree|.
378 topdir: Absolute path to the top of the repo client checkout.
379 dest: Relative path under |topdir| of symlink to create.
380 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700381 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400382 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500383 self.src = src
384 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500385
Wink Saville4c426ef2015-06-03 08:05:17 -0700386 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500387 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700388 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500389 try:
390 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800391 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800392 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500393 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700394 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700395 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500396 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700397 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500398 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700399 _error('Cannot link file %s to %s', relSrc, absDest)
400
401 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400402 """Link the self.src & self.dest paths.
403
404 Handles wild cards on the src linking all of the files in the source in to
405 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700406 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500407 # Some people use src="." to create stable links to projects. Lets allow
408 # that but reject all other uses of "." to keep things simple.
409 if self.src == '.':
410 src = self.git_worktree
411 else:
412 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400413
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300414 if not glob.has_magic(src):
415 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400416 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
417 # dest & src are absolute paths at this point. Make sure the target of
418 # the symlink is relative in the context of the repo client checkout.
419 relpath = os.path.relpath(src, os.path.dirname(dest))
420 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700421 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400422 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300423 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400424 if os.path.exists(dest) and not platform_utils.isdir(dest):
425 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700426 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400427 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700428 # Create a releative path from source dir to destination dir
429 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400430 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700431
432 # Get the source file name
433 srcFile = os.path.basename(absSrcFile)
434
435 # Now form the final full paths to srcFile. They will be
436 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400437 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700438 relSrc = os.path.join(relSrcDir, srcFile)
439 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500440
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700441
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700442class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700443
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700444 def __init__(self,
445 name,
Anthony King7bdac712014-07-16 12:56:40 +0100446 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700447 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100448 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700449 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700450 orig_name=None,
451 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700452 self.name = name
453 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700454 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700455 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100456 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700457 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700458 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700459
Ian Kasprzak0286e312021-02-05 10:06:18 -0800460
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700461class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600462 # These objects can be shared between several working trees.
Mike Frysinger41289c62021-12-20 17:30:33 -0500463 shareable_dirs = ['hooks', 'objects', 'rr-cache']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700464
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700465 def __init__(self,
466 manifest,
467 name,
468 remote,
469 gitdir,
David James8d201162013-10-11 17:03:19 -0700470 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700471 worktree,
472 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700473 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800474 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100475 rebase=True,
476 groups=None,
477 sync_c=False,
478 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900479 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100480 clone_depth=None,
481 upstream=None,
482 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500483 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100484 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900485 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700486 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600487 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700488 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800489 """Init a Project object.
490
491 Args:
492 manifest: The XmlManifest object.
493 name: The `name` attribute of manifest.xml's project element.
494 remote: RemoteSpec object specifying its remote's properties.
495 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700496 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800497 worktree: Absolute path of git working tree.
498 relpath: Relative path of git working tree to repo's top directory.
499 revisionExpr: The `revision` attribute of manifest.xml's project element.
500 revisionId: git commit id for checking out.
501 rebase: The `rebase` attribute of manifest.xml's project element.
502 groups: The `groups` attribute of manifest.xml's project element.
503 sync_c: The `sync-c` attribute of manifest.xml's project element.
504 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900505 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800506 upstream: The `upstream` attribute of manifest.xml's project element.
507 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500508 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800509 is_derived: False if the project was explicitly defined in the manifest;
510 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400511 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900512 optimized_fetch: If True, when a project is set to a sha1 revision, only
513 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600514 retry_fetches: Retry remote fetches n times upon receiving transient error
515 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700516 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800517 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400518 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700519 self.name = name
520 self.remote = remote
Michael Kelly37c21c22020-06-13 02:10:40 -0700521 self.UpdatePaths(relpath, worktree, gitdir, objdir)
Michael Kelly2f3c3312020-07-21 19:40:38 -0700522 self.SetRevision(revisionExpr, revisionId=revisionId)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700523
Mike Pontillod3153822012-02-28 11:53:24 -0800524 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700525 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700526 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800527 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900528 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900529 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700530 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800531 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500532 # NB: Do not use this setting in __init__ to change behavior so that the
533 # manifest.git checkout can inspect & change it after instantiating. See
534 # the XmlManifest init code for more info.
535 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800536 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900537 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600538 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800539 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800540
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700541 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700542 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500543 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500544 self.annotations = []
Bryan Jacobsf609f912013-05-06 13:36:24 -0400545 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700546 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700547
Doug Anderson37282b42011-03-04 11:54:18 -0800548 # This will be filled in if a project is later identified to be the
549 # project containing repo hooks.
550 self.enabled_repo_hooks = []
551
LaMont Jonescc879a92021-11-18 22:40:18 +0000552 def RelPath(self, local=True):
553 """Return the path for the project relative to a manifest.
554
555 Args:
556 local: a boolean, if True, the path is relative to the local
557 (sub)manifest. If false, the path is relative to the
558 outermost manifest.
559 """
560 if local:
561 return self.relpath
562 return os.path.join(self.manifest.path_prefix, self.relpath)
563
Michael Kelly2f3c3312020-07-21 19:40:38 -0700564 def SetRevision(self, revisionExpr, revisionId=None):
565 """Set revisionId based on revision expression and id"""
566 self.revisionExpr = revisionExpr
567 if revisionId is None and revisionExpr and IsId(revisionExpr):
568 self.revisionId = self.revisionExpr
569 else:
570 self.revisionId = revisionId
571
Michael Kelly37c21c22020-06-13 02:10:40 -0700572 def UpdatePaths(self, relpath, worktree, gitdir, objdir):
573 """Update paths used by this project"""
574 self.gitdir = gitdir.replace('\\', '/')
575 self.objdir = objdir.replace('\\', '/')
576 if worktree:
577 self.worktree = os.path.normpath(worktree).replace('\\', '/')
578 else:
579 self.worktree = None
580 self.relpath = relpath
581
582 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
583 defaults=self.manifest.globalConfig)
584
585 if self.worktree:
586 self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir)
587 else:
588 self.work_git = None
589 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir)
590 self.bare_ref = GitRefs(self.gitdir)
591 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
592
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700593 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800594 def Derived(self):
595 return self.is_derived
596
597 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700598 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700599 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700600
601 @property
602 def CurrentBranch(self):
603 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400604
605 The branch name omits the 'refs/heads/' prefix.
606 None is returned if the project is on a detached HEAD, or if the work_git is
607 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700608 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400609 try:
610 b = self.work_git.GetHead()
611 except NoManifestException:
612 # If the local checkout is in a bad state, don't barf. Let the callers
613 # process this like the head is unreadable.
614 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700615 if b.startswith(R_HEADS):
616 return b[len(R_HEADS):]
617 return None
618
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700619 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500620 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
621 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
622 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200623
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700624 def IsDirty(self, consider_untracked=True):
625 """Is the working directory modified in some way?
626 """
627 self.work_git.update_index('-q',
628 '--unmerged',
629 '--ignore-missing',
630 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900631 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700632 return True
633 if self.work_git.DiffZ('diff-files'):
634 return True
635 if consider_untracked and self.work_git.LsOthers():
636 return True
637 return False
638
639 _userident_name = None
640 _userident_email = None
641
642 @property
643 def UserName(self):
644 """Obtain the user's personal name.
645 """
646 if self._userident_name is None:
647 self._LoadUserIdentity()
648 return self._userident_name
649
650 @property
651 def UserEmail(self):
652 """Obtain the user's email address. This is very likely
653 to be their Gerrit login.
654 """
655 if self._userident_email is None:
656 self._LoadUserIdentity()
657 return self._userident_email
658
659 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900660 u = self.bare_git.var('GIT_COMMITTER_IDENT')
661 m = re.compile("^(.*) <([^>]*)> ").match(u)
662 if m:
663 self._userident_name = m.group(1)
664 self._userident_email = m.group(2)
665 else:
666 self._userident_name = ''
667 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700668
669 def GetRemote(self, name):
670 """Get the configuration for a single remote.
671 """
672 return self.config.GetRemote(name)
673
674 def GetBranch(self, name):
675 """Get the configuration for a single branch.
676 """
677 return self.config.GetBranch(name)
678
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700679 def GetBranches(self):
680 """Get all existing local branches.
681 """
682 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900683 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700684 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700685
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530686 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700687 if name.startswith(R_HEADS):
688 name = name[len(R_HEADS):]
689 b = self.GetBranch(name)
690 b.current = name == current
691 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900692 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700693 heads[name] = b
694
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530695 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700696 if name.startswith(R_PUB):
697 name = name[len(R_PUB):]
698 b = heads.get(name)
699 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900700 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700701
702 return heads
703
Colin Cross5acde752012-03-28 20:15:45 -0700704 def MatchesGroups(self, manifest_groups):
705 """Returns true if the manifest groups specified at init should cause
706 this project to be synced.
707 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700708 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700709
710 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700711 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700712 manifest_groups: "-group1,group2"
713 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500714
715 The special manifest group "default" will match any project that
716 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700717 """
LaMont Jones501733c2022-04-20 16:42:32 +0000718 default_groups = self.manifest.default_groups or ['default']
719 expanded_manifest_groups = manifest_groups or default_groups
Conley Owensbb1b5f52012-08-13 13:11:18 -0700720 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700721 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500722 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700723
Conley Owens971de8e2012-04-16 10:36:08 -0700724 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700725 for group in expanded_manifest_groups:
726 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700727 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700728 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700729 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700730
Conley Owens971de8e2012-04-16 10:36:08 -0700731 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700733# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 def UncommitedFiles(self, get_all=True):
735 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700737 Args:
738 get_all: a boolean, if True - get information about all different
739 uncommitted files. If False - return as soon as any kind of
740 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500741 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700742 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500743 self.work_git.update_index('-q',
744 '--unmerged',
745 '--ignore-missing',
746 '--refresh')
747 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700748 details.append("rebase in progress")
749 if not get_all:
750 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500751
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700752 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
753 if changes:
754 details.extend(changes)
755 if not get_all:
756 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500757
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700758 changes = self.work_git.DiffZ('diff-files').keys()
759 if changes:
760 details.extend(changes)
761 if not get_all:
762 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500763
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700764 changes = self.work_git.LsOthers()
765 if changes:
766 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500767
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700768 return details
769
770 def HasChanges(self):
771 """Returns true if there are uncommitted changes.
772 """
773 if self.UncommitedFiles(get_all=False):
774 return True
775 else:
776 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500777
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600778 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700779 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200780
781 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200782 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600783 quiet: If True then only print the project name. Do not print
784 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700785 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700786 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700787 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200788 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700789 print(file=output_redir)
790 print('project %s/' % self.relpath, file=output_redir)
791 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700792 return
793
794 self.work_git.update_index('-q',
795 '--unmerged',
796 '--ignore-missing',
797 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700798 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700799 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
800 df = self.work_git.DiffZ('diff-files')
801 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100802 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700803 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700804
805 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700806 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200807 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700808 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600810 if quiet:
811 out.nl()
812 return 'DIRTY'
813
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700814 branch = self.CurrentBranch
815 if branch is None:
816 out.nobranch('(*** NO BRANCH ***)')
817 else:
818 out.branch('branch %s', branch)
819 out.nl()
820
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700821 if rb:
822 out.important('prior sync failed; rebase still in progress')
823 out.nl()
824
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700825 paths = list()
826 paths.extend(di.keys())
827 paths.extend(df.keys())
828 paths.extend(do)
829
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530830 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900831 try:
832 i = di[p]
833 except KeyError:
834 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700835
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900836 try:
837 f = df[p]
838 except KeyError:
839 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200840
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900841 if i:
842 i_status = i.status.upper()
843 else:
844 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700845
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900846 if f:
847 f_status = f.status.lower()
848 else:
849 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850
851 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800852 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700853 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700854 else:
855 line = ' %s%s\t%s' % (i_status, f_status, p)
856
857 if i and not f:
858 out.added('%s', line)
859 elif (i and f) or (not i and f):
860 out.changed('%s', line)
861 elif not i and not f:
862 out.untracked('%s', line)
863 else:
864 out.write('%s', line)
865 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200866
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700867 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500869 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 """Prints the status of the repository to stdout.
871 """
872 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500873 if output_redir:
874 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875 cmd = ['diff']
876 if out.is_on:
877 cmd.append('--color')
878 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300879 if absolute_paths:
880 cmd.append('--src-prefix=a/%s/' % self.relpath)
881 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400883 try:
884 p = GitCommand(self,
885 cmd,
886 capture_stdout=True,
887 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500888 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400889 except GitError as e:
890 out.nl()
891 out.project('project %s/' % self.relpath)
892 out.nl()
893 out.fail('%s', str(e))
894 out.nl()
895 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500896 if p.stdout:
897 out.nl()
898 out.project('project %s/' % self.relpath)
899 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500900 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400901 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700902
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700903# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900904 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905 """Was the branch published (uploaded) for code review?
906 If so, returns the SHA-1 hash of the last published
907 state for the branch.
908 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700909 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900910 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700911 try:
912 return self.bare_git.rev_parse(key)
913 except GitError:
914 return None
915 else:
916 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900917 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700918 except KeyError:
919 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920
David Pursehouse8a68ff92012-09-24 12:15:13 +0900921 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 """Prunes any stale published refs.
923 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900924 if all_refs is None:
925 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926 heads = set()
927 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530928 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929 if name.startswith(R_HEADS):
930 heads.add(name)
931 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900932 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530934 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 n = name[len(R_PUB):]
936 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900937 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700939 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700940 """List any branches which can be uploaded for review.
941 """
942 heads = {}
943 pubed = {}
944
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530945 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700946 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900947 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700948 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900949 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950
951 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530952 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900953 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700955 if selected_branch and branch != selected_branch:
956 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800958 rb = self.GetUploadableBranch(branch)
959 if rb:
960 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961 return ready
962
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800963 def GetUploadableBranch(self, branch_name):
964 """Get a single uploadable branch, or None.
965 """
966 branch = self.GetBranch(branch_name)
967 base = branch.LocalMerge
968 if branch.LocalMerge:
969 rb = ReviewableBranch(self, branch, base)
970 if rb.commits:
971 return rb
972 return None
973
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700974 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100975 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500976 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700977 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500978 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500979 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200980 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700981 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200982 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200983 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800984 validate_certs=True,
985 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986 """Uploads the named branch for code review.
987 """
988 if branch is None:
989 branch = self.CurrentBranch
990 if branch is None:
991 raise GitError('not currently on a branch')
992
993 branch = self.GetBranch(branch)
994 if not branch.LocalMerge:
995 raise GitError('branch %s does not track a remote' % branch.name)
996 if not branch.remote.review:
997 raise GitError('remote %s has no review url' % branch.remote.name)
998
Mike Frysinger3a0a1452022-05-20 12:52:33 -0400999 # Basic validity check on label syntax.
1000 for label in labels:
1001 if not re.match(r'^.+[+-][0-9]+$', label):
1002 raise UploadError(
1003 f'invalid label syntax "{label}": labels use forms like '
1004 'CodeReview+1 or Verified-1')
1005
Bryan Jacobsf609f912013-05-06 13:36:24 -04001006 if dest_branch is None:
1007 dest_branch = self.dest_branch
1008 if dest_branch is None:
1009 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001010 if not dest_branch.startswith(R_HEADS):
1011 dest_branch = R_HEADS + dest_branch
1012
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001013 if not branch.remote.projectname:
1014 branch.remote.projectname = self.name
1015 branch.remote.Save()
1016
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001017 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001018 if url is None:
1019 raise UploadError('review not configured')
1020 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -05001021 if dryrun:
1022 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001023
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001024 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001025 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001026
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001027 for push_option in (push_options or []):
1028 cmd.append('-o')
1029 cmd.append(push_option)
1030
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001031 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001032
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001033 if dest_branch.startswith(R_HEADS):
1034 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001035
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001036 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001037 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001038 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001039 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001040 opts += ['t=%s' % p for p in hashtags]
Mike Frysinger3a0a1452022-05-20 12:52:33 -04001041 # NB: No need to encode labels as they've been validated above.
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001042 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001043
David Pursehousef25a3702018-11-14 19:01:22 -08001044 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001045 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001046 if notify:
1047 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001048 if private:
1049 opts += ['private']
1050 if wip:
1051 opts += ['wip']
1052 if opts:
1053 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001054 cmd.append(ref_spec)
1055
Anthony King7bdac712014-07-16 12:56:40 +01001056 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001057 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001058
Mike Frysingerd7f86832020-11-19 19:18:46 -05001059 if not dryrun:
1060 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1061 self.bare_git.UpdateRef(R_PUB + branch.name,
1062 R_HEADS + branch.name,
1063 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001065# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001066 def _ExtractArchive(self, tarpath, path=None):
1067 """Extract the given tar on its current location
1068
1069 Args:
1070 - tarpath: The path to the actual tar file
1071
1072 """
1073 try:
1074 with tarfile.open(tarpath, 'r') as tar:
1075 tar.extractall(path=path)
1076 return True
1077 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001078 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001079 return False
1080
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001081 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001082 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001083 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001084 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001085 is_new=None,
Mike Frysinger73561142021-05-03 01:10:09 -04001086 current_branch_only=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001087 force_sync=False,
1088 clone_bundle=True,
Mike Frysingerd68ed632021-05-03 01:21:35 -04001089 tags=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001090 archive=False,
1091 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001092 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001093 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001094 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001095 ssh_proxy=None,
Raman Tennetif32f2432021-04-12 20:57:25 -07001096 clone_filter=None,
Raman Tenneticd89ec12021-04-22 09:18:14 -07001097 partial_clone_exclude=set()):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 """Perform only the network IO portion of the sync process.
1099 Local working directory/branch state is not affected.
1100 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001101 if archive and not isinstance(self, MetaProject):
1102 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001103 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001104 return False
1105
1106 name = self.relpath.replace('\\', '/')
1107 name = name.replace('/', '_')
1108 tarpath = '%s.tar' % name
1109 topdir = self.manifest.topdir
1110
1111 try:
1112 self._FetchArchive(tarpath, cwd=topdir)
1113 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001114 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001115 return False
1116
1117 # From now on, we only need absolute tarpath
1118 tarpath = os.path.join(topdir, tarpath)
1119
1120 if not self._ExtractArchive(tarpath, path=topdir):
1121 return False
1122 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001123 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001124 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001125 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001126 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001127 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001128
1129 # If the shared object dir already exists, don't try to rebootstrap with a
1130 # clone bundle download. We should have the majority of objects already.
1131 if clone_bundle and os.path.exists(self.objdir):
1132 clone_bundle = False
1133
Raman Tennetif32f2432021-04-12 20:57:25 -07001134 if self.name in partial_clone_exclude:
1135 clone_bundle = True
1136 clone_filter = None
1137
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001138 if is_new is None:
1139 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001140 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001141 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001142 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001143 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001144 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001145
1146 if is_new:
Mike Frysinger152032c2021-12-20 21:17:43 -05001147 alt = os.path.join(self.objdir, 'objects/info/alternates')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001148 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001149 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001150 # This works for both absolute and relative alternate directories.
1151 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001152 except IOError:
1153 alt_dir = None
1154 else:
1155 alt_dir = None
1156
Mike Frysingere50b6a72020-02-19 01:45:48 -05001157 if (clone_bundle
1158 and alt_dir is None
1159 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001160 is_new = False
1161
Mike Frysinger73561142021-05-03 01:10:09 -04001162 if current_branch_only is None:
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001163 if self.sync_c:
1164 current_branch_only = True
1165 elif not self.manifest._loaded:
1166 # Manifest cannot check defaults until it syncs.
1167 current_branch_only = False
1168 elif self.manifest.default.sync_c:
1169 current_branch_only = True
1170
Mike Frysingerd68ed632021-05-03 01:21:35 -04001171 if tags is None:
1172 tags = self.sync_tags
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001173
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001174 if self.clone_depth:
1175 depth = self.clone_depth
1176 else:
LaMont Jonesd82be3e2022-04-05 19:30:46 +00001177 depth = self.manifest.manifestProject.depth
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001178
Mike Frysinger521d01b2020-02-17 01:51:49 -05001179 # See if we can skip the network fetch entirely.
1180 if not (optimized_fetch and
1181 (ID_RE.match(self.revisionExpr) and
1182 self._CheckForImmutableRevision())):
1183 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001184 initial=is_new,
1185 quiet=quiet, verbose=verbose, output_redir=output_redir,
1186 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001187 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001188 submodules=submodules, force_sync=force_sync,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001189 ssh_proxy=ssh_proxy,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001190 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001191 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001192
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001193 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +00001194 dissociate = mp.dissociate
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001195 if dissociate:
Mike Frysinger152032c2021-12-20 21:17:43 -05001196 alternates_file = os.path.join(self.objdir, 'objects/info/alternates')
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001197 if os.path.exists(alternates_file):
1198 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001199 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1200 merge_output=bool(output_redir))
1201 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001202 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001203 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001204 return False
1205 platform_utils.remove(alternates_file)
1206
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001207 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001208 self._InitMRef()
1209 else:
1210 self._InitMirrorHead()
Mike Frysinger9d96f582021-09-28 11:27:24 -04001211 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
1212 missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001213 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001214
1215 def PostRepoUpgrade(self):
1216 self._InitHooks()
1217
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001218 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001219 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001220 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001221 for copyfile in self.copyfiles:
1222 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001223 for linkfile in self.linkfiles:
1224 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001225
Julien Camperguedd654222014-01-09 16:21:37 +01001226 def GetCommitRevisionId(self):
1227 """Get revisionId of a commit.
1228
1229 Use this method instead of GetRevisionId to get the id of the commit rather
1230 than the id of the current git object (for example, a tag)
1231
1232 """
1233 if not self.revisionExpr.startswith(R_TAGS):
1234 return self.GetRevisionId(self._allrefs)
1235
1236 try:
1237 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1238 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001239 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1240 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001241
David Pursehouse8a68ff92012-09-24 12:15:13 +09001242 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001243 if self.revisionId:
1244 return self.revisionId
1245
1246 rem = self.GetRemote(self.remote.name)
1247 rev = rem.ToLocal(self.revisionExpr)
1248
David Pursehouse8a68ff92012-09-24 12:15:13 +09001249 if all_refs is not None and rev in all_refs:
1250 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001251
1252 try:
1253 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1254 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001255 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1256 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001257
Raman Tenneti6a872c92021-01-14 19:17:50 -08001258 def SetRevisionId(self, revisionId):
Xin Li0e776a52021-06-29 21:42:34 +00001259 if self.revisionExpr:
Xin Liaabf79d2021-04-29 01:50:38 -07001260 self.upstream = self.revisionExpr
1261
Raman Tenneti6a872c92021-01-14 19:17:50 -08001262 self.revisionId = revisionId
1263
Martin Kellye4e94d22017-03-21 16:05:12 -07001264 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001265 """Perform only the local IO portion of the sync process.
1266 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001267 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001268 if not os.path.exists(self.gitdir):
1269 syncbuf.fail(self,
1270 'Cannot checkout %s due to missing network sync; Run '
1271 '`repo sync -n %s` first.' %
1272 (self.name, self.name))
1273 return
1274
Martin Kellye4e94d22017-03-21 16:05:12 -07001275 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001276 all_refs = self.bare_ref.all
1277 self.CleanPublishedCache(all_refs)
1278 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001279
Mike Frysinger0458faa2021-03-10 23:35:44 -05001280 # Special case the root of the repo client checkout. Make sure it doesn't
1281 # contain files being checked out to dirs we don't allow.
1282 if self.relpath == '.':
1283 PROTECTED_PATHS = {'.repo'}
1284 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1285 bad_paths = paths & PROTECTED_PATHS
1286 if bad_paths:
1287 syncbuf.fail(self,
1288 'Refusing to checkout project that writes to protected '
1289 'paths: %s' % (', '.join(bad_paths),))
1290 return
1291
David Pursehouse1d947b32012-10-25 12:23:11 +09001292 def _doff():
1293 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001294 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001295
Martin Kellye4e94d22017-03-21 16:05:12 -07001296 def _dosubmodules():
1297 self._SyncSubmodules(quiet=True)
1298
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001299 head = self.work_git.GetHead()
1300 if head.startswith(R_HEADS):
1301 branch = head[len(R_HEADS):]
1302 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001303 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001304 except KeyError:
1305 head = None
1306 else:
1307 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001308
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001309 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001310 # Currently on a detached HEAD. The user is assumed to
1311 # not have any local modifications worth worrying about.
1312 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001313 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001314 syncbuf.fail(self, _PriorSyncFailedError())
1315 return
1316
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001317 if head == revid:
1318 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001319 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001320 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001321 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001322 # The copy/linkfile config may have changed.
1323 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001324 return
1325 else:
1326 lost = self._revlist(not_rev(revid), HEAD)
1327 if lost:
1328 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001329
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001330 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001331 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001332 if submodules:
1333 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001334 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001335 syncbuf.fail(self, e)
1336 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001337 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001338 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001339
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001340 if head == revid:
1341 # No changes; don't do anything further.
1342 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001343 # The copy/linkfile config may have changed.
1344 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001345 return
1346
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001347 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001348
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001349 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001350 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001351 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001352 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001353 syncbuf.info(self,
1354 "leaving %s; does not track upstream",
1355 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001356 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001357 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001358 if submodules:
1359 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001360 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001361 syncbuf.fail(self, e)
1362 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001363 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001364 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001365
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001366 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001367
1368 # See if we can perform a fast forward merge. This can happen if our
1369 # branch isn't in the exact same state as we last published.
1370 try:
1371 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1372 # Skip the published logic.
1373 pub = False
1374 except GitError:
1375 pub = self.WasPublished(branch.name, all_refs)
1376
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001377 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001378 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001379 if not_merged:
1380 if upstream_gain:
1381 # The user has published this branch and some of those
1382 # commits are not yet merged upstream. We do not want
1383 # to rewrite the published commits so we punt.
1384 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001385 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001386 "branch %s is published (but not merged) and is now "
1387 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001388 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001389 elif pub == head:
1390 # All published commits are merged, and thus we are a
1391 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001392 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001393 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001394 if submodules:
1395 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001396 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001397
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001398 # Examine the local commits not in the remote. Find the
1399 # last one attributed to this user, if any.
1400 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001401 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001402 last_mine = None
1403 cnt_mine = 0
1404 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001405 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001406 if committer_email == self.UserEmail:
1407 last_mine = commit_id
1408 cnt_mine += 1
1409
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001410 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001411 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001412
1413 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001414 syncbuf.fail(self, _DirtyError())
1415 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001416
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001417 # If the upstream switched on us, warn the user.
1418 #
1419 if branch.merge != self.revisionExpr:
1420 if branch.merge and self.revisionExpr:
1421 syncbuf.info(self,
1422 'manifest switched %s...%s',
1423 branch.merge,
1424 self.revisionExpr)
1425 elif branch.merge:
1426 syncbuf.info(self,
1427 'manifest no longer tracks %s',
1428 branch.merge)
1429
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001430 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001431 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001432 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001433 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001434 syncbuf.info(self,
1435 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001436 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001437
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001438 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001439 if not ID_RE.match(self.revisionExpr):
1440 # in case of manifest sync the revisionExpr might be a SHA1
1441 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001442 if not branch.merge.startswith('refs/'):
1443 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001444 branch.Save()
1445
Mike Pontillod3153822012-02-28 11:53:24 -08001446 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001447 def _docopyandlink():
1448 self._CopyAndLinkFiles()
1449
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001450 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001451 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001452 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001453 if submodules:
1454 syncbuf.later2(self, _dosubmodules)
1455 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001456 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001457 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001458 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001459 if submodules:
1460 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001461 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001462 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001463 syncbuf.fail(self, e)
1464 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001466 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001467 if submodules:
1468 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001469
Mike Frysingere6a202f2019-08-02 15:57:57 -04001470 def AddCopyFile(self, src, dest, topdir):
1471 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001472
Mike Frysingere6a202f2019-08-02 15:57:57 -04001473 No filesystem changes occur here. Actual copying 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 copy happens.
1477 """
1478 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1479
1480 def AddLinkFile(self, src, dest, topdir):
1481 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1482
1483 No filesystem changes occur here. Actual linking happens later on.
1484
1485 Paths should have basic validation run on them before being queued.
1486 Further checking will be handled when the actual link happens.
1487 """
1488 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001489
James W. Mills24c13082012-04-12 15:04:13 -05001490 def AddAnnotation(self, name, value, keep):
Jack Neus6ea0cae2021-07-20 20:52:33 +00001491 self.annotations.append(Annotation(name, value, keep))
James W. Mills24c13082012-04-12 15:04:13 -05001492
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001493 def DownloadPatchSet(self, change_id, patch_id):
1494 """Download a single patch set of a single change to FETCH_HEAD.
1495 """
1496 remote = self.GetRemote(self.remote.name)
1497
1498 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001499 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001500 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001501 if GitCommand(self, cmd, bare=True).Wait() != 0:
1502 return None
1503 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001504 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001505 change_id,
1506 patch_id,
1507 self.bare_git.rev_parse('FETCH_HEAD'))
1508
Mike Frysingerc0d18662020-02-19 19:19:18 -05001509 def DeleteWorktree(self, quiet=False, force=False):
1510 """Delete the source checkout and any other housekeeping tasks.
1511
1512 This currently leaves behind the internal .repo/ cache state. This helps
1513 when switching branches or manifest changes get reverted as we don't have
1514 to redownload all the git objects. But we should do some GC at some point.
1515
1516 Args:
1517 quiet: Whether to hide normal messages.
1518 force: Always delete tree even if dirty.
1519
1520 Returns:
1521 True if the worktree was completely cleaned out.
1522 """
1523 if self.IsDirty():
1524 if force:
1525 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1526 (self.relpath,), file=sys.stderr)
1527 else:
1528 print('error: %s: Cannot remove project: uncommitted changes are '
1529 'present.\n' % (self.relpath,), file=sys.stderr)
1530 return False
1531
1532 if not quiet:
1533 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1534
1535 # Unlock and delink from the main worktree. We don't use git's worktree
1536 # remove because it will recursively delete projects -- we handle that
1537 # ourselves below. https://crbug.com/git/48
1538 if self.use_git_worktrees:
1539 needle = platform_utils.realpath(self.gitdir)
1540 # Find the git worktree commondir under .repo/worktrees/.
1541 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1542 assert output.startswith('worktree '), output
1543 commondir = output[9:]
1544 # Walk each of the git worktrees to see where they point.
1545 configs = os.path.join(commondir, 'worktrees')
1546 for name in os.listdir(configs):
1547 gitdir = os.path.join(configs, name, 'gitdir')
1548 with open(gitdir) as fp:
1549 relpath = fp.read().strip()
1550 # Resolve the checkout path and see if it matches this project.
1551 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1552 if fullpath == needle:
1553 platform_utils.rmtree(os.path.join(configs, name))
1554
1555 # Delete the .git directory first, so we're less likely to have a partially
1556 # working git repository around. There shouldn't be any git projects here,
1557 # so rmtree works.
1558
1559 # Try to remove plain files first in case of git worktrees. If this fails
1560 # for any reason, we'll fall back to rmtree, and that'll display errors if
1561 # it can't remove things either.
1562 try:
1563 platform_utils.remove(self.gitdir)
1564 except OSError:
1565 pass
1566 try:
1567 platform_utils.rmtree(self.gitdir)
1568 except OSError as e:
1569 if e.errno != errno.ENOENT:
1570 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1571 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1572 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1573 return False
1574
1575 # Delete everything under the worktree, except for directories that contain
1576 # another git project.
1577 dirs_to_remove = []
1578 failed = False
1579 for root, dirs, files in platform_utils.walk(self.worktree):
1580 for f in files:
1581 path = os.path.join(root, f)
1582 try:
1583 platform_utils.remove(path)
1584 except OSError as e:
1585 if e.errno != errno.ENOENT:
1586 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1587 failed = True
1588 dirs[:] = [d for d in dirs
1589 if not os.path.lexists(os.path.join(root, d, '.git'))]
1590 dirs_to_remove += [os.path.join(root, d) for d in dirs
1591 if os.path.join(root, d) not in dirs_to_remove]
1592 for d in reversed(dirs_to_remove):
1593 if platform_utils.islink(d):
1594 try:
1595 platform_utils.remove(d)
1596 except OSError as e:
1597 if e.errno != errno.ENOENT:
1598 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1599 failed = True
1600 elif not platform_utils.listdir(d):
1601 try:
1602 platform_utils.rmdir(d)
1603 except OSError as e:
1604 if e.errno != errno.ENOENT:
1605 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1606 failed = True
1607 if failed:
1608 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1609 file=sys.stderr)
1610 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1611 return False
1612
1613 # Try deleting parent dirs if they are empty.
1614 path = self.worktree
1615 while path != self.manifest.topdir:
1616 try:
1617 platform_utils.rmdir(path)
1618 except OSError as e:
1619 if e.errno != errno.ENOENT:
1620 break
1621 path = os.path.dirname(path)
1622
1623 return True
1624
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001625# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001626 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627 """Create a new branch off the manifest's revision.
1628 """
Simran Basib9a1b732015-08-20 12:19:28 -07001629 if not branch_merge:
1630 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001631 head = self.work_git.GetHead()
1632 if head == (R_HEADS + name):
1633 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001634
David Pursehouse8a68ff92012-09-24 12:15:13 +09001635 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001636 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001637 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001638 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001639 capture_stdout=True,
1640 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001641
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001642 branch = self.GetBranch(name)
1643 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001644 branch.merge = branch_merge
1645 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1646 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001647
1648 if revision is None:
1649 revid = self.GetRevisionId(all_refs)
1650 else:
1651 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001652
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001653 if head.startswith(R_HEADS):
1654 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001655 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001656 except KeyError:
1657 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001658 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001659 ref = R_HEADS + name
1660 self.work_git.update_ref(ref, revid)
1661 self.work_git.symbolic_ref(HEAD, ref)
1662 branch.Save()
1663 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001664
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001665 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001666 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001667 capture_stdout=True,
1668 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001669 branch.Save()
1670 return True
1671 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001672
Wink Saville02d79452009-04-10 13:01:24 -07001673 def CheckoutBranch(self, name):
1674 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001675
1676 Args:
1677 name: The name of the branch to checkout.
1678
1679 Returns:
1680 True if the checkout succeeded; False if it didn't; None if the branch
1681 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001682 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001683 rev = R_HEADS + name
1684 head = self.work_git.GetHead()
1685 if head == rev:
1686 # Already on the branch
1687 #
1688 return True
Wink Saville02d79452009-04-10 13:01:24 -07001689
David Pursehouse8a68ff92012-09-24 12:15:13 +09001690 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001691 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001692 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001693 except KeyError:
1694 # Branch does not exist in this project
1695 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001696 return None
Wink Saville02d79452009-04-10 13:01:24 -07001697
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001698 if head.startswith(R_HEADS):
1699 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001700 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001701 except KeyError:
1702 head = None
1703
1704 if head == revid:
1705 # Same revision; just update HEAD to point to the new
1706 # target branch, but otherwise take no other action.
1707 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001708 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1709 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001710 return True
1711
1712 return GitCommand(self,
1713 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001714 capture_stdout=True,
1715 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001716
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001717 def AbandonBranch(self, name):
1718 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001719
1720 Args:
1721 name: The name of the branch to abandon.
1722
1723 Returns:
1724 True if the abandon succeeded; False if it didn't; None if the branch
1725 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001726 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001727 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001728 all_refs = self.bare_ref.all
1729 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001730 # Doesn't exist
1731 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001732
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001733 head = self.work_git.GetHead()
1734 if head == rev:
1735 # We can't destroy the branch while we are sitting
1736 # on it. Switch to a detached HEAD.
1737 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001738 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001739
David Pursehouse8a68ff92012-09-24 12:15:13 +09001740 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001741 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001742 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001743 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001744 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001745
1746 return GitCommand(self,
1747 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001748 capture_stdout=True,
1749 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001750
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001751 def PruneHeads(self):
1752 """Prune any topic branches already merged into upstream.
1753 """
1754 cb = self.CurrentBranch
1755 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001756 left = self._allrefs
1757 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758 if name.startswith(R_HEADS):
1759 name = name[len(R_HEADS):]
1760 if cb is None or name != cb:
1761 kill.append(name)
1762
Mike Frysingera3794e92021-03-11 23:24:01 -05001763 # Minor optimization: If there's nothing to prune, then don't try to read
1764 # any project state.
1765 if not kill and not cb:
1766 return []
1767
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001768 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001769 if cb is not None \
1770 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001771 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001772 self.work_git.DetachHead(HEAD)
1773 kill.append(cb)
1774
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001775 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001776 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001777
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001778 try:
1779 self.bare_git.DetachHead(rev)
1780
1781 b = ['branch', '-d']
1782 b.extend(kill)
1783 b = GitCommand(self, b, bare=True,
1784 capture_stdout=True,
1785 capture_stderr=True)
1786 b.Wait()
1787 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001788 if ID_RE.match(old):
1789 self.bare_git.DetachHead(old)
1790 else:
1791 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001792 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001793
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001794 for branch in kill:
1795 if (R_HEADS + branch) not in left:
1796 self.CleanPublishedCache()
1797 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001798
1799 if cb and cb not in kill:
1800 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001801 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001802
1803 kept = []
1804 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001805 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001806 branch = self.GetBranch(branch)
1807 base = branch.LocalMerge
1808 if not base:
1809 base = rev
1810 kept.append(ReviewableBranch(self, branch, base))
1811 return kept
1812
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001813# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001814 def GetRegisteredSubprojects(self):
1815 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001816
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001817 def rec(subprojects):
1818 if not subprojects:
1819 return
1820 result.extend(subprojects)
1821 for p in subprojects:
1822 rec(p.subprojects)
1823 rec(self.subprojects)
1824 return result
1825
1826 def _GetSubmodules(self):
1827 # Unfortunately we cannot call `git submodule status --recursive` here
1828 # because the working tree might not exist yet, and it cannot be used
1829 # without a working tree in its current implementation.
1830
1831 def get_submodules(gitdir, rev):
1832 # Parse .gitmodules for submodule sub_paths and sub_urls
1833 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1834 if not sub_paths:
1835 return []
1836 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1837 # revision of submodule repository
1838 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1839 submodules = []
1840 for sub_path, sub_url in zip(sub_paths, sub_urls):
1841 try:
1842 sub_rev = sub_revs[sub_path]
1843 except KeyError:
1844 # Ignore non-exist submodules
1845 continue
1846 submodules.append((sub_rev, sub_path, sub_url))
1847 return submodules
1848
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001849 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1850 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001851
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001852 def parse_gitmodules(gitdir, rev):
1853 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1854 try:
Anthony King7bdac712014-07-16 12:56:40 +01001855 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1856 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001857 except GitError:
1858 return [], []
1859 if p.Wait() != 0:
1860 return [], []
1861
1862 gitmodules_lines = []
1863 fd, temp_gitmodules_path = tempfile.mkstemp()
1864 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001865 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001866 os.close(fd)
1867 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001868 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1869 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001870 if p.Wait() != 0:
1871 return [], []
1872 gitmodules_lines = p.stdout.split('\n')
1873 except GitError:
1874 return [], []
1875 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001876 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001877
1878 names = set()
1879 paths = {}
1880 urls = {}
1881 for line in gitmodules_lines:
1882 if not line:
1883 continue
1884 m = re_path.match(line)
1885 if m:
1886 names.add(m.group(1))
1887 paths[m.group(1)] = m.group(2)
1888 continue
1889 m = re_url.match(line)
1890 if m:
1891 names.add(m.group(1))
1892 urls[m.group(1)] = m.group(2)
1893 continue
1894 names = sorted(names)
1895 return ([paths.get(name, '') for name in names],
1896 [urls.get(name, '') for name in names])
1897
1898 def git_ls_tree(gitdir, rev, paths):
1899 cmd = ['ls-tree', rev, '--']
1900 cmd.extend(paths)
1901 try:
Anthony King7bdac712014-07-16 12:56:40 +01001902 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1903 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001904 except GitError:
1905 return []
1906 if p.Wait() != 0:
1907 return []
1908 objects = {}
1909 for line in p.stdout.split('\n'):
1910 if not line.strip():
1911 continue
1912 object_rev, object_path = line.split()[2:4]
1913 objects[object_path] = object_rev
1914 return objects
1915
1916 try:
1917 rev = self.GetRevisionId()
1918 except GitError:
1919 return []
1920 return get_submodules(self.gitdir, rev)
1921
1922 def GetDerivedSubprojects(self):
1923 result = []
1924 if not self.Exists:
1925 # If git repo does not exist yet, querying its submodules will
1926 # mess up its states; so return here.
1927 return result
1928 for rev, path, url in self._GetSubmodules():
1929 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001930 relpath, worktree, gitdir, objdir = \
1931 self.manifest.GetSubprojectPaths(self, name, path)
1932 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001933 if project:
1934 result.extend(project.GetDerivedSubprojects())
1935 continue
David James8d201162013-10-11 17:03:19 -07001936
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001937 if url.startswith('..'):
1938 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001939 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001940 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001941 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001942 review=self.remote.review,
1943 revision=self.remote.revision)
1944 subproject = Project(manifest=self.manifest,
1945 name=name,
1946 remote=remote,
1947 gitdir=gitdir,
1948 objdir=objdir,
1949 worktree=worktree,
1950 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001951 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001952 revisionId=rev,
1953 rebase=self.rebase,
1954 groups=self.groups,
1955 sync_c=self.sync_c,
1956 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001957 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001958 parent=self,
1959 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001960 result.append(subproject)
1961 result.extend(subproject.GetDerivedSubprojects())
1962 return result
1963
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001964# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001965 def EnableRepositoryExtension(self, key, value='true', version=1):
1966 """Enable git repository extension |key| with |value|.
1967
1968 Args:
1969 key: The extension to enabled. Omit the "extensions." prefix.
1970 value: The value to use for the extension.
1971 version: The minimum git repository version needed.
1972 """
1973 # Make sure the git repo version is new enough already.
1974 found_version = self.config.GetInt('core.repositoryFormatVersion')
1975 if found_version is None:
1976 found_version = 0
1977 if found_version < version:
1978 self.config.SetString('core.repositoryFormatVersion', str(version))
1979
1980 # Enable the extension!
1981 self.config.SetString('extensions.%s' % (key,), value)
1982
Mike Frysinger50a81de2020-09-06 15:51:21 -04001983 def ResolveRemoteHead(self, name=None):
1984 """Find out what the default branch (HEAD) points to.
1985
1986 Normally this points to refs/heads/master, but projects are moving to main.
1987 Support whatever the server uses rather than hardcoding "master" ourselves.
1988 """
1989 if name is None:
1990 name = self.remote.name
1991
1992 # The output will look like (NB: tabs are separators):
1993 # ref: refs/heads/master HEAD
1994 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1995 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1996
1997 for line in output.splitlines():
1998 lhs, rhs = line.split('\t', 1)
1999 if rhs == 'HEAD' and lhs.startswith('ref:'):
2000 return lhs[4:].strip()
2001
2002 return None
2003
Zac Livingstone4332262017-06-16 08:56:09 -06002004 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002005 try:
2006 # if revision (sha or tag) is not present then following function
2007 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08002008 self.bare_git.rev_list('-1', '--missing=allow-any',
2009 '%s^0' % self.revisionExpr, '--')
Xin Li0e776a52021-06-29 21:42:34 +00002010 if self.upstream:
2011 rev = self.GetRemote(self.remote.name).ToLocal(self.upstream)
2012 self.bare_git.rev_list('-1', '--missing=allow-any',
2013 '%s^0' % rev, '--')
Xin Li8e983bb2021-07-20 20:15:30 +00002014 self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev)
Chris AtLee2fb64662014-01-16 21:32:33 -05002015 return True
2016 except GitError:
2017 # There is no such persistent revision. We have to fetch it.
2018 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002019
Julien Campergue335f5ef2013-10-16 11:02:35 +02002020 def _FetchArchive(self, tarpath, cwd=None):
2021 cmd = ['archive', '-v', '-o', tarpath]
2022 cmd.append('--remote=%s' % self.remote.url)
2023 cmd.append('--prefix=%s/' % self.relpath)
2024 cmd.append(self.revisionExpr)
2025
2026 command = GitCommand(self, cmd, cwd=cwd,
2027 capture_stdout=True,
2028 capture_stderr=True)
2029
2030 if command.Wait() != 0:
2031 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2032
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002033 def _RemoteFetch(self, name=None,
2034 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002035 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002036 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002037 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002038 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002039 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002040 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002041 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002042 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002043 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002044 ssh_proxy=None,
Xin Li745be2e2019-06-03 11:24:30 -07002045 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002046 clone_filter=None,
2047 retry_fetches=2,
2048 retry_sleep_initial_sec=4.0,
2049 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002050 is_sha1 = False
2051 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002052 # The depth should not be used when fetching to a mirror because
2053 # it will result in a shallow repository that cannot be cloned or
2054 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002055 # The repo project should also never be synced with partial depth.
2056 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2057 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002058
Shawn Pearce69e04d82014-01-29 12:48:54 -08002059 if depth:
2060 current_branch_only = True
2061
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002062 if ID_RE.match(self.revisionExpr) is not None:
2063 is_sha1 = True
2064
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002065 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002066 if self.revisionExpr.startswith(R_TAGS):
Robin Schneider9c1fc5b2021-11-13 22:55:32 +01002067 # This is a tag and its commit id should never change.
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002068 tag_name = self.revisionExpr[len(R_TAGS):]
Robin Schneider9c1fc5b2021-11-13 22:55:32 +01002069 elif self.upstream and self.upstream.startswith(R_TAGS):
2070 # This is a tag and its commit id should never change.
2071 tag_name = self.upstream[len(R_TAGS):]
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002072
2073 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002074 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002075 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002076 print('Skipped fetching project %s (already have persistent ref)'
2077 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002078 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002079 if is_sha1 and not depth:
2080 # When syncing a specific commit and --depth is not set:
2081 # * if upstream is explicitly specified and is not a sha1, fetch only
2082 # upstream as users expect only upstream to be fetch.
2083 # Note: The commit might not be in upstream in which case the sync
2084 # will fail.
2085 # * otherwise, fetch all branches to make sure we end up with the
2086 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002087 if self.upstream:
2088 current_branch_only = not ID_RE.match(self.upstream)
2089 else:
2090 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002091
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002092 if not name:
2093 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002094
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002095 remote = self.GetRemote(name)
Mike Frysinger339f2df2021-05-06 00:44:42 -04002096 if not remote.PreConnectFetch(ssh_proxy):
2097 ssh_proxy = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002098
Shawn O. Pearce88443382010-10-08 10:02:09 +02002099 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002100 if alt_dir and 'objects' == os.path.basename(alt_dir):
2101 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002102 packed_refs = os.path.join(self.gitdir, 'packed-refs')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002103
David Pursehouse8a68ff92012-09-24 12:15:13 +09002104 all_refs = self.bare_ref.all
2105 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 tmp = set()
2107
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302108 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002109 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002110 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002111 all_refs[r] = ref_id
2112 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002113 continue
2114
David Pursehouse8a68ff92012-09-24 12:15:13 +09002115 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002116 continue
2117
David Pursehouse8a68ff92012-09-24 12:15:13 +09002118 r = 'refs/_alt/%s' % ref_id
2119 all_refs[r] = ref_id
2120 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121 tmp.add(r)
2122
heping3d7bbc92017-04-12 19:51:47 +08002123 tmp_packed_lines = []
2124 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302126 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002127 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002128 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002129 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002130 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002131
heping3d7bbc92017-04-12 19:51:47 +08002132 tmp_packed = ''.join(tmp_packed_lines)
2133 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002135 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002136 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002137
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002138 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002139
Xin Li745be2e2019-06-03 11:24:30 -07002140 if clone_filter:
2141 git_require((2, 19, 0), fail=True, msg='partial clones')
2142 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002143 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002144
Conley Owensf97e8382015-01-21 11:12:46 -08002145 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002146 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002147 else:
2148 # If this repo has shallow objects, then we don't know which refs have
2149 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2150 # do this with projects that don't have shallow objects, since it is less
2151 # efficient.
2152 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2153 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002154
Mike Frysinger4847e052020-02-22 00:07:35 -05002155 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002156 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002157 if not quiet and sys.stdout.isatty():
2158 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002159 if not self.worktree:
2160 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002161 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002162
Mike Frysingere57f1142019-03-18 21:27:54 -04002163 if force_sync:
2164 cmd.append('--force')
2165
David Pursehouse74cfd272015-10-14 10:50:15 +09002166 if prune:
2167 cmd.append('--prune')
2168
LaMont Jonesadaa1d82022-02-10 17:34:36 +00002169 cmd.append(f'--recurse-submodules={"on-demand" if submodules else "no"}')
Martin Kellye4e94d22017-03-21 16:05:12 -07002170
Kuang-che Wu6856f982019-11-25 12:37:55 +08002171 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002172 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002173 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002174 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002175 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002176 spec.append('tag')
2177 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002178
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302179 if self.manifest.IsMirror and not current_branch_only:
2180 branch = None
2181 else:
2182 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002183 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002184 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002185 # Shallow checkout of a specific commit, fetch from that commit and not
2186 # the heads only as the commit might be deeper in the history.
2187 spec.append(branch)
Xin Liaabf79d2021-04-29 01:50:38 -07002188 if self.upstream:
2189 spec.append(self.upstream)
Kuang-che Wu6856f982019-11-25 12:37:55 +08002190 else:
2191 if is_sha1:
2192 branch = self.upstream
2193 if branch is not None and branch.strip():
2194 if not branch.startswith('refs/'):
2195 branch = R_HEADS + branch
2196 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2197
2198 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2199 # whole repo.
2200 if self.manifest.IsMirror and not spec:
2201 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2202
2203 # If using depth then we should not get all the tags since they may
2204 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002205 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002206 cmd.append('--no-tags')
2207 else:
2208 cmd.append('--tags')
2209 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2210
Conley Owens80b87fe2014-05-09 17:13:44 -07002211 cmd.extend(spec)
2212
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002213 # At least one retry minimum due to git remote prune.
2214 retry_fetches = max(retry_fetches, 2)
2215 retry_cur_sleep = retry_sleep_initial_sec
2216 ok = prune_tried = False
2217 for try_n in range(retry_fetches):
Mike Frysinger67d6cdf2021-12-23 17:36:09 -05002218 gitcmd = GitCommand(
2219 self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects'),
2220 ssh_proxy=ssh_proxy,
2221 merge_output=True, capture_stdout=quiet or bool(output_redir))
Mike Frysinger7b586f22021-02-23 18:38:39 -05002222 if gitcmd.stdout and not quiet and output_redir:
2223 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002224 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002225 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002226 ok = True
2227 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002228
2229 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002230 elif (gitcmd.stdout and
2231 'error:' in gitcmd.stdout and
2232 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002233 # Fallthru to sleep+retry logic at the bottom.
2234 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002235
Mike Frysinger6823bc22021-04-15 02:06:28 -04002236 # Try to prune remote branches once in case there are conflicts.
2237 # For example, if the remote had refs/heads/upstream, but deleted that and
2238 # now has refs/heads/upstream/foo.
2239 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002240 'error:' in gitcmd.stdout and
2241 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002242 not prune_tried):
2243 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002244 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002245 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002246 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002247 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002248 break
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002249 print('retrying fetch after pruning remote branches', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002250 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002251 continue
Brian Harring14a66742012-09-28 20:21:57 -07002252 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002253 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2254 # in sha1 mode, we just tried sync'ing from the upstream field; it
2255 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002256 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002257 elif ret < 0:
2258 # Git died with a signal, exit immediately
2259 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002260
2261 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002262 if not verbose and gitcmd.stdout:
2263 print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002264 if try_n < retry_fetches - 1:
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002265 print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep),
2266 file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002267 time.sleep(retry_cur_sleep)
2268 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2269 MAXIMUM_RETRY_SLEEP_SEC)
2270 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2271 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002272
2273 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002274 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002275 if old_packed != '':
2276 _lwrite(packed_refs, old_packed)
2277 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002278 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002279 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002280
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002281 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002282 # We just synced the upstream given branch; verify we
2283 # got what we wanted, else trigger a second run of all
2284 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002285 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002286 # Sync the current branch only with depth set to None.
2287 # We always pass depth=None down to avoid infinite recursion.
2288 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002289 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002290 current_branch_only=current_branch_only and depth,
2291 initial=False, alt_dir=alt_dir,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002292 depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002293
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002294 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002295
Mike Frysingere50b6a72020-02-19 01:45:48 -05002296 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
LaMont Jonesd82be3e2022-04-05 19:30:46 +00002297 if initial and (self.manifest.manifestProject.depth or self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002298 return False
2299
2300 remote = self.GetRemote(self.remote.name)
2301 bundle_url = remote.url + '/clone.bundle'
2302 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002303 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2304 'persistent-http',
2305 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002306 return False
2307
2308 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2309 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2310
2311 exist_dst = os.path.exists(bundle_dst)
2312 exist_tmp = os.path.exists(bundle_tmp)
2313
2314 if not initial and not exist_dst and not exist_tmp:
2315 return False
2316
2317 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002318 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2319 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002320 if not exist_dst:
2321 return False
2322
2323 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002324 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002325 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002326 if not quiet and sys.stdout.isatty():
2327 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002328 if not self.worktree:
2329 cmd.append('--update-head-ok')
2330 cmd.append(bundle_dst)
2331 for f in remote.fetch:
2332 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002333 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002334
Mike Frysinger67d6cdf2021-12-23 17:36:09 -05002335 ok = GitCommand(
2336 self, cmd, bare=True, objdir=os.path.join(self.objdir, 'objects')).Wait() == 0
Mike Frysinger9d96f582021-09-28 11:27:24 -04002337 platform_utils.remove(bundle_dst, missing_ok=True)
2338 platform_utils.remove(bundle_tmp, missing_ok=True)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002339 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002340
Mike Frysingere50b6a72020-02-19 01:45:48 -05002341 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002342 platform_utils.remove(dstPath, missing_ok=True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002343
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002344 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002345 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002346 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002347 if os.path.exists(tmpPath):
2348 size = os.stat(tmpPath).st_size
2349 if size >= 1024:
2350 cmd += ['--continue-at', '%d' % (size,)]
2351 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002352 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002353 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002354 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002355 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002356 if proxy:
2357 cmd += ['--proxy', proxy]
2358 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2359 cmd += ['--proxy', os.environ['http_proxy']]
2360 if srcUrl.startswith('persistent-https'):
2361 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2362 elif srcUrl.startswith('persistent-http'):
2363 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002364 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002365
Dave Borowitz137d0132015-01-02 11:12:54 -08002366 if IsTrace():
2367 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002368 if verbose:
2369 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2370 stdout = None if verbose else subprocess.PIPE
2371 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002372 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002373 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002374 except OSError:
2375 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002376
Mike Frysingere50b6a72020-02-19 01:45:48 -05002377 (output, _) = proc.communicate()
2378 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002379
Dave Borowitz137d0132015-01-02 11:12:54 -08002380 if curlret == 22:
2381 # From curl man page:
2382 # 22: HTTP page not retrieved. The requested url was not found or
2383 # returned another error with the HTTP error code being 400 or above.
2384 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002385 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002386 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2387 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002388 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002389 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002390 elif curlret and not verbose and output:
2391 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002392
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002393 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002394 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002395 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002396 return True
2397 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002398 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002399 return False
2400 else:
2401 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002402
Kris Giesingc8d882a2014-12-23 13:02:32 -08002403 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002404 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002405 with open(path, 'rb') as f:
2406 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002407 return True
2408 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002409 if not quiet:
2410 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002411 return False
2412 except OSError:
2413 return False
2414
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002415 def _Checkout(self, rev, quiet=False):
2416 cmd = ['checkout']
2417 if quiet:
2418 cmd.append('-q')
2419 cmd.append(rev)
2420 cmd.append('--')
2421 if GitCommand(self, cmd).Wait() != 0:
2422 if self._allrefs:
2423 raise GitError('%s checkout %s ' % (self.name, rev))
2424
Mike Frysinger915fda12020-03-22 12:15:20 -04002425 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002426 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002427 if ffonly:
2428 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002429 if record_origin:
2430 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002431 cmd.append(rev)
2432 cmd.append('--')
2433 if GitCommand(self, cmd).Wait() != 0:
2434 if self._allrefs:
2435 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2436
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302437 def _LsRemote(self, refs):
2438 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302439 p = GitCommand(self, cmd, capture_stdout=True)
2440 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002441 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302442 return None
2443
Anthony King7bdac712014-07-16 12:56:40 +01002444 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002445 cmd = ['revert']
2446 cmd.append('--no-edit')
2447 cmd.append(rev)
2448 cmd.append('--')
2449 if GitCommand(self, cmd).Wait() != 0:
2450 if self._allrefs:
2451 raise GitError('%s revert %s ' % (self.name, rev))
2452
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002453 def _ResetHard(self, rev, quiet=True):
2454 cmd = ['reset', '--hard']
2455 if quiet:
2456 cmd.append('-q')
2457 cmd.append(rev)
2458 if GitCommand(self, cmd).Wait() != 0:
2459 raise GitError('%s reset --hard %s ' % (self.name, rev))
2460
Martin Kellye4e94d22017-03-21 16:05:12 -07002461 def _SyncSubmodules(self, quiet=True):
2462 cmd = ['submodule', 'update', '--init', '--recursive']
2463 if quiet:
2464 cmd.append('-q')
2465 if GitCommand(self, cmd).Wait() != 0:
LaMont Jones7b9b2512021-11-03 20:48:27 +00002466 raise GitError('%s submodule update --init --recursive ' % self.name)
Martin Kellye4e94d22017-03-21 16:05:12 -07002467
Anthony King7bdac712014-07-16 12:56:40 +01002468 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002469 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002470 if onto is not None:
2471 cmd.extend(['--onto', onto])
2472 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002473 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002474 raise GitError('%s rebase %s ' % (self.name, upstream))
2475
Pierre Tardy3d125942012-05-04 12:18:12 +02002476 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002477 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002478 if ffonly:
2479 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002480 if GitCommand(self, cmd).Wait() != 0:
2481 raise GitError('%s merge %s ' % (self.name, head))
2482
David Pursehousee8ace262020-02-13 12:41:15 +09002483 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002484 init_git_dir = not os.path.exists(self.gitdir)
2485 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002486 try:
2487 # Initialize the bare repository, which contains all of the objects.
2488 if init_obj_dir:
2489 os.makedirs(self.objdir)
2490 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002491
Mike Frysinger333c0a42021-11-15 12:39:00 -05002492 self._UpdateHooks(quiet=quiet)
2493
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002494 if self.use_git_worktrees:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002495 # Enable per-worktree config file support if possible. This is more a
2496 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002497 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002498 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002499
Kevin Degib1a07b82015-07-27 13:33:43 -06002500 # If we have a separate directory to hold refs, initialize it as well.
2501 if self.objdir != self.gitdir:
2502 if init_git_dir:
2503 os.makedirs(self.gitdir)
2504
2505 if init_obj_dir or init_git_dir:
Mike Frysingerc72bd842021-11-14 03:58:00 -05002506 self._ReferenceGitDir(self.objdir, self.gitdir, copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002507 try:
Mike Frysingerc72bd842021-11-14 03:58:00 -05002508 self._CheckDirReference(self.objdir, self.gitdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002509 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002510 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002511 print("Retrying clone after deleting %s" %
2512 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002513 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002514 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2515 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002516 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002517 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002518 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2519 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002520 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002521 raise e
2522 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002523
Kevin Degi384b3c52014-10-16 16:02:58 -06002524 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002525 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +00002526 ref_dir = mp.reference or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002527
LaMont Jonescc879a92021-11-18 22:40:18 +00002528 def _expanded_ref_dirs():
2529 """Iterate through the possible git reference directory paths."""
2530 name = self.name + '.git'
2531 yield mirror_git or os.path.join(ref_dir, name)
2532 for prefix in '', self.remote.name:
2533 yield os.path.join(ref_dir, '.repo', 'project-objects', prefix, name)
2534 yield os.path.join(ref_dir, '.repo', 'worktrees', prefix, name)
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002535
LaMont Jonescc879a92021-11-18 22:40:18 +00002536 if ref_dir or mirror_git:
2537 found_ref_dir = None
2538 for path in _expanded_ref_dirs():
2539 if os.path.exists(path):
2540 found_ref_dir = path
2541 break
2542 ref_dir = found_ref_dir
Shawn O. Pearce88443382010-10-08 10:02:09 +02002543
Kevin Degib1a07b82015-07-27 13:33:43 -06002544 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002545 if not os.path.isabs(ref_dir):
2546 # The alternate directory is relative to the object database.
2547 ref_dir = os.path.relpath(ref_dir,
2548 os.path.join(self.objdir, 'objects'))
Mike Frysinger152032c2021-12-20 21:17:43 -05002549 _lwrite(os.path.join(self.objdir, 'objects/info/alternates'),
Kevin Degib1a07b82015-07-27 13:33:43 -06002550 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002551
Kevin Degib1a07b82015-07-27 13:33:43 -06002552 m = self.manifest.manifestProject.config
2553 for key in ['user.name', 'user.email']:
2554 if m.Has(key, include_defaults=False):
2555 self.config.SetString(key, m.GetString(key))
XD Trol630876f2022-01-17 23:29:04 +08002556 if not self.manifest.EnableGitLfs:
2557 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
2558 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002559 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002560 except Exception:
2561 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002562 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002563 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002564 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002565 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002566
David Pursehousee8ace262020-02-13 12:41:15 +09002567 def _UpdateHooks(self, quiet=False):
Mike Frysinger333c0a42021-11-15 12:39:00 -05002568 if os.path.exists(self.objdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002569 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002570
David Pursehousee8ace262020-02-13 12:41:15 +09002571 def _InitHooks(self, quiet=False):
Mike Frysinger333c0a42021-11-15 12:39:00 -05002572 hooks = platform_utils.realpath(os.path.join(self.objdir, 'hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002573 if not os.path.exists(hooks):
2574 os.makedirs(hooks)
Mike Frysinger98bb7652021-12-20 21:15:59 -05002575
2576 # Delete sample hooks. They're noise.
2577 for hook in glob.glob(os.path.join(hooks, '*.sample')):
Peter Kjellerstedtb5505012022-01-21 23:09:19 +01002578 try:
2579 platform_utils.remove(hook, missing_ok=True)
2580 except PermissionError:
2581 pass
Mike Frysinger98bb7652021-12-20 21:15:59 -05002582
Jonathan Nieder93719792015-03-17 11:29:58 -07002583 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002584 name = os.path.basename(stock_hook)
2585
Victor Boivie65e0f352011-04-18 11:23:29 +02002586 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002587 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002588 # Don't install a Gerrit Code Review hook if this
2589 # project does not appear to use it for reviews.
2590 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002591 # Since the manifest project is one of those, but also
2592 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002593 continue
2594
2595 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002596 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002597 continue
2598 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002599 # If the files are the same, we'll leave it alone. We create symlinks
2600 # below by default but fallback to hardlinks if the OS blocks them.
2601 # So if we're here, it's probably because we made a hardlink below.
2602 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002603 if not quiet:
2604 _warn("%s: Not replacing locally modified %s hook",
2605 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002606 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002607 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002608 platform_utils.symlink(
2609 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002610 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002611 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002612 try:
2613 os.link(stock_hook, dst)
2614 except OSError:
2615 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002616 else:
2617 raise
2618
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002619 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002620 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002621 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002622 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002623 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002624 remote.review = self.remote.review
2625 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002626
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002627 if self.worktree:
2628 remote.ResetFetch(mirror=False)
2629 else:
2630 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002631 remote.Save()
2632
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002633 def _InitMRef(self):
2634 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002635 if self.use_git_worktrees:
Mike Frysinger29626b42021-05-01 09:37:13 -04002636 # Set up the m/ space to point to the worktree-specific ref space.
2637 # We'll update the worktree-specific ref space on each checkout.
2638 ref = R_M + self.manifest.branch
2639 if not self.bare_ref.symref(ref):
2640 self.bare_git.symbolic_ref(
2641 '-m', 'redirecting to worktree scope',
2642 ref, R_WORKTREE_M + self.manifest.branch)
2643
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002644 # We can't update this ref with git worktrees until it exists.
2645 # We'll wait until the initial checkout to set it.
2646 if not os.path.exists(self.worktree):
2647 return
2648
2649 base = R_WORKTREE_M
2650 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002651
2652 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002653 else:
2654 base = R_M
2655 active_git = self.bare_git
2656
2657 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002658
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002659 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002660 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002661
Remy Böhmer1469c282020-12-15 18:49:02 +01002662 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002663 cur = self.bare_ref.symref(ref)
2664
2665 if self.revisionId:
2666 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2667 msg = 'manifest set to %s' % self.revisionId
2668 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002669 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002670 else:
2671 remote = self.GetRemote(self.remote.name)
2672 dst = remote.ToLocal(self.revisionExpr)
2673 if cur != dst:
2674 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002675 if detach:
2676 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2677 else:
2678 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002679
Mike Frysingerc72bd842021-11-14 03:58:00 -05002680 def _CheckDirReference(self, srcdir, destdir):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002681 # Git worktrees don't use symlinks to share at all.
2682 if self.use_git_worktrees:
2683 return
2684
Mike Frysingerd33dce02021-12-20 18:16:33 -05002685 for name in self.shareable_dirs:
Mike Frysingered4f2112020-02-11 23:06:29 -05002686 # Try to self-heal a bit in simple cases.
2687 dst_path = os.path.join(destdir, name)
2688 src_path = os.path.join(srcdir, name)
2689
Mike Frysingered4f2112020-02-11 23:06:29 -05002690 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002691 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002692 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002693 # Fail if the links are pointing to the wrong place
2694 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002695 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002696 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002697 'work tree. If you\'re comfortable with the '
2698 'possibility of losing the work tree\'s git metadata,'
2699 ' use `repo sync --force-sync {0}` to '
2700 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002701
Mike Frysingerc72bd842021-11-14 03:58:00 -05002702 def _ReferenceGitDir(self, gitdir, dotgit, copy_all):
David James8d201162013-10-11 17:03:19 -07002703 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2704
2705 Args:
2706 gitdir: The bare git repository. Must already be initialized.
2707 dotgit: The repository you would like to initialize.
David James8d201162013-10-11 17:03:19 -07002708 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2709 This saves you the effort of initializing |dotgit| yourself.
2710 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002711 symlink_dirs = self.shareable_dirs[:]
Mike Frysingerd33dce02021-12-20 18:16:33 -05002712 to_symlink = symlink_dirs
David James8d201162013-10-11 17:03:19 -07002713
2714 to_copy = []
2715 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002716 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002717
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002718 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002719 for name in set(to_copy).union(to_symlink):
2720 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002721 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002722 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002723
Kevin Degi384b3c52014-10-16 16:02:58 -06002724 if os.path.lexists(dst):
2725 continue
David James8d201162013-10-11 17:03:19 -07002726
2727 # If the source dir doesn't exist, create an empty dir.
2728 if name in symlink_dirs and not os.path.lexists(src):
2729 os.makedirs(src)
2730
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002731 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002732 platform_utils.symlink(
2733 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002734 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002735 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002736 shutil.copytree(src, dst)
2737 elif os.path.isfile(src):
2738 shutil.copy(src, dst)
2739
David James8d201162013-10-11 17:03:19 -07002740 except OSError as e:
2741 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002742 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002743 else:
2744 raise
2745
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002746 def _InitGitWorktree(self):
2747 """Init the project using git worktrees."""
2748 self.bare_git.worktree('prune')
2749 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2750 self.worktree, self.GetRevisionId())
2751
2752 # Rewrite the internal state files to use relative paths between the
2753 # checkouts & worktrees.
2754 dotgit = os.path.join(self.worktree, '.git')
2755 with open(dotgit, 'r') as fp:
2756 # Figure out the checkout->worktree path.
2757 setting = fp.read()
2758 assert setting.startswith('gitdir:')
2759 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002760 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2761 # of file permissions. Delete it and recreate it from scratch to avoid.
2762 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002763 # Use relative path from checkout->worktree & maintain Unix line endings
2764 # on all OS's to match git behavior.
2765 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002766 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2767 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002768 # Use relative path from worktree->checkout & maintain Unix line endings
2769 # on all OS's to match git behavior.
2770 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002771 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2772
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002773 self._InitMRef()
2774
Martin Kellye4e94d22017-03-21 16:05:12 -07002775 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002776 """Setup the worktree .git path.
2777
2778 This is the user-visible path like src/foo/.git/.
2779
2780 With non-git-worktrees, this will be a symlink to the .repo/projects/ path.
2781 With git-worktrees, this will be a .git file using "gitdir: ..." syntax.
2782
2783 Older checkouts had .git/ directories. If we see that, migrate it.
2784
2785 This also handles changes in the manifest. Maybe this project was backed
2786 by "foo/bar" on the server, but now it's "new/foo/bar". We have to update
2787 the path we point to under .repo/projects/ to match.
2788 """
2789 dotgit = os.path.join(self.worktree, '.git')
2790
2791 # If using an old layout style (a directory), migrate it.
2792 if not platform_utils.islink(dotgit) and platform_utils.isdir(dotgit):
2793 self._MigrateOldWorkTreeGitDir(dotgit)
2794
2795 init_dotgit = not os.path.exists(dotgit)
2796 if self.use_git_worktrees:
2797 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002798 self._InitGitWorktree()
2799 self._CopyAndLinkFiles()
Mike Frysingerf4545122019-11-11 04:34:16 -05002800 else:
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002801 if not init_dotgit:
2802 # See if the project has changed.
2803 if platform_utils.realpath(self.gitdir) != platform_utils.realpath(dotgit):
2804 platform_utils.remove(dotgit)
Mike Frysingerf4545122019-11-11 04:34:16 -05002805
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002806 if init_dotgit or not os.path.exists(dotgit):
2807 os.makedirs(self.worktree, exist_ok=True)
2808 platform_utils.symlink(os.path.relpath(self.gitdir, self.worktree), dotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002809
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002810 if init_dotgit:
2811 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002812
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002813 # Finish checking out the worktree.
2814 cmd = ['read-tree', '--reset', '-u', '-v', HEAD]
2815 if GitCommand(self, cmd).Wait() != 0:
2816 raise GitError('Cannot initialize work tree for ' + self.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002817
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002818 if submodules:
2819 self._SyncSubmodules(quiet=True)
2820 self._CopyAndLinkFiles()
Victor Boivie0960b5b2010-11-26 13:42:13 +01002821
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002822 @classmethod
2823 def _MigrateOldWorkTreeGitDir(cls, dotgit):
2824 """Migrate the old worktree .git/ dir style to a symlink.
2825
2826 This logic specifically only uses state from |dotgit| to figure out where to
2827 move content and not |self|. This way if the backing project also changed
2828 places, we only do the .git/ dir to .git symlink migration here. The path
2829 updates will happen independently.
2830 """
2831 # Figure out where in .repo/projects/ it's pointing to.
2832 if not os.path.islink(os.path.join(dotgit, 'refs')):
2833 raise GitError(f'{dotgit}: unsupported checkout state')
2834 gitdir = os.path.dirname(os.path.realpath(os.path.join(dotgit, 'refs')))
2835
2836 # Remove known symlink paths that exist in .repo/projects/.
2837 KNOWN_LINKS = {
2838 'config', 'description', 'hooks', 'info', 'logs', 'objects',
2839 'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn',
2840 }
2841 # Paths that we know will be in both, but are safe to clobber in .repo/projects/.
2842 SAFE_TO_CLOBBER = {
Mike Frysinger8e912482022-01-26 04:03:34 -05002843 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'gc.log', 'gitk.cache', 'index',
2844 'ORIG_HEAD',
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002845 }
2846
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002847 # First see if we'd succeed before starting the migration.
2848 unknown_paths = []
2849 for name in platform_utils.listdir(dotgit):
2850 # Ignore all temporary/backup names. These are common with vim & emacs.
2851 if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
2852 continue
2853
2854 dotgit_path = os.path.join(dotgit, name)
2855 if name in KNOWN_LINKS:
2856 if not platform_utils.islink(dotgit_path):
2857 unknown_paths.append(f'{dotgit_path}: should be a symlink')
2858 else:
2859 gitdir_path = os.path.join(gitdir, name)
2860 if name not in SAFE_TO_CLOBBER and os.path.exists(gitdir_path):
2861 unknown_paths.append(f'{dotgit_path}: unknown file; please file a bug')
2862 if unknown_paths:
2863 raise GitError('Aborting migration: ' + '\n'.join(unknown_paths))
2864
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002865 # Now walk the paths and sync the .git/ to .repo/projects/.
2866 for name in platform_utils.listdir(dotgit):
2867 dotgit_path = os.path.join(dotgit, name)
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002868
2869 # Ignore all temporary/backup names. These are common with vim & emacs.
2870 if name.endswith('~') or (name[0] == '#' and name[-1] == '#'):
2871 platform_utils.remove(dotgit_path)
2872 elif name in KNOWN_LINKS:
2873 platform_utils.remove(dotgit_path)
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002874 else:
2875 gitdir_path = os.path.join(gitdir, name)
Mike Frysinger89ed8ac2022-01-06 05:42:24 -05002876 platform_utils.remove(gitdir_path, missing_ok=True)
2877 platform_utils.rename(dotgit_path, gitdir_path)
Mike Frysinger2a089cf2021-11-13 23:29:42 -05002878
2879 # Now that the dir should be empty, clear it out, and symlink it over.
2880 platform_utils.rmdir(dotgit)
2881 platform_utils.symlink(os.path.relpath(gitdir, os.path.dirname(dotgit)), dotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002882
Renaud Paquay788e9622017-01-27 11:41:12 -08002883 def _get_symlink_error_message(self):
2884 if platform_utils.isWindows():
2885 return ('Unable to create symbolic link. Please re-run the command as '
2886 'Administrator, or see '
2887 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2888 'for other options.')
2889 return 'filesystem must support symlinks'
2890
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002891 def _revlist(self, *args, **kw):
2892 a = []
2893 a.extend(args)
2894 a.append('--')
2895 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002896
2897 @property
2898 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002899 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002900
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002901 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002902 """Get logs between two revisions of this project."""
2903 comp = '..'
2904 if rev1:
2905 revs = [rev1]
2906 if rev2:
2907 revs.extend([comp, rev2])
2908 cmd = ['log', ''.join(revs)]
2909 out = DiffColoring(self.config)
2910 if out.is_on and color:
2911 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002912 if pretty_format is not None:
2913 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002914 if oneline:
2915 cmd.append('--oneline')
2916
2917 try:
2918 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2919 if log.Wait() == 0:
2920 return log.stdout
2921 except GitError:
2922 # worktree may not exist if groups changed for example. In that case,
2923 # try in gitdir instead.
2924 if not os.path.exists(self.worktree):
2925 return self.bare_git.log(*cmd[1:])
2926 else:
2927 raise
2928 return None
2929
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002930 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2931 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002932 """Get the list of logs from this revision to given revisionId"""
2933 logs = {}
2934 selfId = self.GetRevisionId(self._allrefs)
2935 toId = toProject.GetRevisionId(toProject._allrefs)
2936
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002937 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2938 pretty_format=pretty_format)
2939 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2940 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002941 return logs
2942
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002944
David James8d201162013-10-11 17:03:19 -07002945 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002946 self._project = project
2947 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002948 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002949
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002950 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2951 def __getstate__(self):
2952 return (self._project, self._bare, self._gitdir)
2953
2954 def __setstate__(self, state):
2955 self._project, self._bare, self._gitdir = state
2956
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002957 def LsOthers(self):
2958 p = GitCommand(self._project,
2959 ['ls-files',
2960 '-z',
2961 '--others',
2962 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002963 bare=False,
David James8d201162013-10-11 17:03:19 -07002964 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002965 capture_stdout=True,
2966 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002967 if p.Wait() == 0:
2968 out = p.stdout
2969 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002970 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002971 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002972 return []
2973
2974 def DiffZ(self, name, *args):
2975 cmd = [name]
2976 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002977 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002978 cmd.extend(args)
2979 p = GitCommand(self._project,
2980 cmd,
David James8d201162013-10-11 17:03:19 -07002981 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002982 bare=False,
2983 capture_stdout=True,
2984 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002985 p.Wait()
2986 r = {}
2987 out = p.stdout
2988 if out:
2989 out = iter(out[:-1].split('\0'))
2990 while out:
2991 try:
2992 info = next(out)
2993 path = next(out)
2994 except StopIteration:
2995 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002996
Mike Frysinger84230002021-02-16 17:08:35 -05002997 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002998
Mike Frysinger84230002021-02-16 17:08:35 -05002999 def __init__(self, path, omode, nmode, oid, nid, state):
3000 self.path = path
3001 self.src_path = None
3002 self.old_mode = omode
3003 self.new_mode = nmode
3004 self.old_id = oid
3005 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003006
Mike Frysinger84230002021-02-16 17:08:35 -05003007 if len(state) == 1:
3008 self.status = state
3009 self.level = None
3010 else:
3011 self.status = state[:1]
3012 self.level = state[1:]
3013 while self.level.startswith('0'):
3014 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003015
Mike Frysinger84230002021-02-16 17:08:35 -05003016 info = info[1:].split(' ')
3017 info = _Info(path, *info)
3018 if info.status in ('R', 'C'):
3019 info.src_path = info.path
3020 info.path = next(out)
3021 r[info.path] = info
3022 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003023
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003024 def GetDotgitPath(self, subpath=None):
3025 """Return the full path to the .git dir.
3026
3027 As a convenience, append |subpath| if provided.
3028 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003029 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003030 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003031 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05003032 dotgit = os.path.join(self._project.worktree, '.git')
3033 if os.path.isfile(dotgit):
3034 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
3035 with open(dotgit) as fp:
3036 setting = fp.read()
3037 assert setting.startswith('gitdir:')
3038 gitdir = setting.split(':', 1)[1].strip()
3039 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
3040
3041 return dotgit if subpath is None else os.path.join(dotgit, subpath)
3042
3043 def GetHead(self):
3044 """Return the ref that HEAD points to."""
3045 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08003046 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05003047 with open(path) as fd:
3048 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04003049 except IOError as e:
3050 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07003051 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303052 line = line.decode()
3053 except AttributeError:
3054 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003055 if line.startswith('ref: '):
3056 return line[5:-1]
3057 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003058
3059 def SetHead(self, ref, message=None):
3060 cmdv = []
3061 if message is not None:
3062 cmdv.extend(['-m', message])
3063 cmdv.append(HEAD)
3064 cmdv.append(ref)
3065 self.symbolic_ref(*cmdv)
3066
3067 def DetachHead(self, new, message=None):
3068 cmdv = ['--no-deref']
3069 if message is not None:
3070 cmdv.extend(['-m', message])
3071 cmdv.append(HEAD)
3072 cmdv.append(new)
3073 self.update_ref(*cmdv)
3074
3075 def UpdateRef(self, name, new, old=None,
3076 message=None,
3077 detach=False):
3078 cmdv = []
3079 if message is not None:
3080 cmdv.extend(['-m', message])
3081 if detach:
3082 cmdv.append('--no-deref')
3083 cmdv.append(name)
3084 cmdv.append(new)
3085 if old is not None:
3086 cmdv.append(old)
3087 self.update_ref(*cmdv)
3088
3089 def DeleteRef(self, name, old=None):
3090 if not old:
3091 old = self.rev_parse(name)
3092 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003093 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003094
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003095 def rev_list(self, *args, **kw):
3096 if 'format' in kw:
3097 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3098 else:
3099 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003100 cmdv.extend(args)
3101 p = GitCommand(self._project,
3102 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003103 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003104 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003105 capture_stdout=True,
3106 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003107 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003108 raise GitError('%s rev-list %s: %s' %
3109 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003110 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003111
3112 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003113 """Allow arbitrary git commands using pythonic syntax.
3114
3115 This allows you to do things like:
3116 git_obj.rev_parse('HEAD')
3117
3118 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3119 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003120 Any other positional arguments will be passed to the git command, and the
3121 following keyword arguments are supported:
3122 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003123
3124 Args:
3125 name: The name of the git command to call. Any '_' characters will
3126 be replaced with '-'.
3127
3128 Returns:
3129 A callable object that will try to call git with the named command.
3130 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003131 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003132
Dave Borowitz091f8932012-10-23 17:01:04 -07003133 def runner(*args, **kwargs):
3134 cmdv = []
3135 config = kwargs.pop('config', None)
3136 for k in kwargs:
3137 raise TypeError('%s() got an unexpected keyword argument %r'
3138 % (name, k))
3139 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303140 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003141 cmdv.append('-c')
3142 cmdv.append('%s=%s' % (k, v))
3143 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003144 cmdv.extend(args)
3145 p = GitCommand(self._project,
3146 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003147 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003148 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003149 capture_stdout=True,
3150 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003151 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003152 raise GitError('%s %s: %s' %
3153 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003154 r = p.stdout
3155 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3156 return r[:-1]
3157 return r
3158 return runner
3159
3160
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003161class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003162
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003163 def __str__(self):
3164 return 'prior sync failed; rebase still in progress'
3165
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003166
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003167class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003168
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003169 def __str__(self):
3170 return 'contains uncommitted changes'
3171
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003172
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003173class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003174
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003175 def __init__(self, project, text):
3176 self.project = project
3177 self.text = text
3178
3179 def Print(self, syncbuf):
3180 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3181 syncbuf.out.nl()
3182
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003183
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003184class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003185
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003186 def __init__(self, project, why):
3187 self.project = project
3188 self.why = why
3189
3190 def Print(self, syncbuf):
3191 syncbuf.out.fail('error: %s/: %s',
3192 self.project.relpath,
3193 str(self.why))
3194 syncbuf.out.nl()
3195
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003196
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003197class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003198
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003199 def __init__(self, project, action):
3200 self.project = project
3201 self.action = action
3202
3203 def Run(self, syncbuf):
3204 out = syncbuf.out
3205 out.project('project %s/', self.project.relpath)
3206 out.nl()
3207 try:
3208 self.action()
3209 out.nl()
3210 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003211 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003212 out.nl()
3213 return False
3214
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003215
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003216class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003217
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003218 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003219 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003220 self.project = self.printer('header', attr='bold')
3221 self.info = self.printer('info')
3222 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003223
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003224
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003225class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003226
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003227 def __init__(self, config, detach_head=False):
3228 self._messages = []
3229 self._failures = []
3230 self._later_queue1 = []
3231 self._later_queue2 = []
3232
3233 self.out = _SyncColoring(config)
3234 self.out.redirect(sys.stderr)
3235
3236 self.detach_head = detach_head
3237 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003238 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003239
3240 def info(self, project, fmt, *args):
3241 self._messages.append(_InfoMessage(project, fmt % args))
3242
3243 def fail(self, project, err=None):
3244 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003245 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003246
3247 def later1(self, project, what):
3248 self._later_queue1.append(_Later(project, what))
3249
3250 def later2(self, project, what):
3251 self._later_queue2.append(_Later(project, what))
3252
3253 def Finish(self):
3254 self._PrintMessages()
3255 self._RunLater()
3256 self._PrintMessages()
3257 return self.clean
3258
David Rileye0684ad2017-04-05 00:02:59 -07003259 def Recently(self):
3260 recent_clean = self.recent_clean
3261 self.recent_clean = True
3262 return recent_clean
3263
3264 def _MarkUnclean(self):
3265 self.clean = False
3266 self.recent_clean = False
3267
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003268 def _RunLater(self):
3269 for q in ['_later_queue1', '_later_queue2']:
3270 if not self._RunQueue(q):
3271 return
3272
3273 def _RunQueue(self, queue):
3274 for m in getattr(self, queue):
3275 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003276 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003277 return False
3278 setattr(self, queue, [])
3279 return True
3280
3281 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003282 if self._messages or self._failures:
3283 if os.isatty(2):
3284 self.out.write(progress.CSI_ERASE_LINE)
3285 self.out.write('\r')
3286
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003287 for m in self._messages:
3288 m.Print(self)
3289 for m in self._failures:
3290 m.Print(self)
3291
3292 self._messages = []
3293 self._failures = []
3294
3295
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003296class MetaProject(Project):
LaMont Jones9b72cf22022-03-29 21:54:22 +00003297 """A special project housed under .repo."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003298
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003299 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003300 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003301 manifest=manifest,
3302 name=name,
3303 gitdir=gitdir,
3304 objdir=gitdir,
3305 worktree=worktree,
3306 remote=RemoteSpec('origin'),
3307 relpath='.repo/%s' % name,
3308 revisionExpr='refs/heads/master',
3309 revisionId=None,
3310 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003311
3312 def PreSync(self):
3313 if self.Exists:
3314 cb = self.CurrentBranch
3315 if cb:
3316 base = self.GetBranch(cb).merge
3317 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003318 self.revisionExpr = base
3319 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003320
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003321 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003322 def HasChanges(self):
LaMont Jones9b72cf22022-03-29 21:54:22 +00003323 """Has the remote received new commits not yet checked out?"""
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003324 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003325 return False
3326
David Pursehouse8a68ff92012-09-24 12:15:13 +09003327 all_refs = self.bare_ref.all
3328 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003329 head = self.work_git.GetHead()
3330 if head.startswith(R_HEADS):
3331 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003332 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003333 except KeyError:
3334 head = None
3335
3336 if revid == head:
3337 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003338 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003339 return True
3340 return False
LaMont Jones9b72cf22022-03-29 21:54:22 +00003341
3342
3343class RepoProject(MetaProject):
3344 """The MetaProject for repo itself."""
3345
3346 @property
3347 def LastFetch(self):
3348 try:
3349 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3350 return os.path.getmtime(fh)
3351 except OSError:
3352 return 0
3353
3354class ManifestProject(MetaProject):
3355 """The MetaProject for manifests."""
3356
3357 def MetaBranchSwitch(self, submodules=False):
3358 """Prepare for manifest branch switch."""
3359
3360 # detach and delete manifest branch, allowing a new
3361 # branch to take over
3362 syncbuf = SyncBuffer(self.config, detach_head=True)
3363 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3364 syncbuf.Finish()
3365
3366 return GitCommand(self,
3367 ['update-ref', '-d', 'refs/heads/default'],
3368 capture_stdout=True,
3369 capture_stderr=True).Wait() == 0
LaMont Jones9b03f152022-03-29 23:01:18 +00003370
3371 @property
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003372 def standalone_manifest_url(self):
3373 """The URL of the standalone manifest, or None."""
LaMont Jones55ee3042022-04-06 17:10:21 +00003374 return self.config.GetString('manifest.standalone')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003375
3376 @property
3377 def manifest_groups(self):
3378 """The manifest groups string."""
3379 return self.config.GetString('manifest.groups')
3380
3381 @property
3382 def reference(self):
3383 """The --reference for this manifest."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003384 return self.config.GetString('repo.reference')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003385
3386 @property
3387 def dissociate(self):
3388 """Whether to dissociate."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003389 return self.config.GetBoolean('repo.dissociate')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003390
3391 @property
3392 def archive(self):
3393 """Whether we use archive."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003394 return self.config.GetBoolean('repo.archive')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003395
3396 @property
3397 def mirror(self):
3398 """Whether we use mirror."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003399 return self.config.GetBoolean('repo.mirror')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003400
3401 @property
3402 def use_worktree(self):
3403 """Whether we use worktree."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003404 return self.config.GetBoolean('repo.worktree')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003405
3406 @property
3407 def clone_bundle(self):
3408 """Whether we use clone_bundle."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003409 return self.config.GetBoolean('repo.clonebundle')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003410
3411 @property
3412 def submodules(self):
3413 """Whether we use submodules."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003414 return self.config.GetBoolean('repo.submodules')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003415
3416 @property
3417 def git_lfs(self):
3418 """Whether we use git_lfs."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003419 return self.config.GetBoolean('repo.git-lfs')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003420
3421 @property
3422 def use_superproject(self):
3423 """Whether we use superproject."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003424 return self.config.GetBoolean('repo.superproject')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003425
3426 @property
3427 def partial_clone(self):
3428 """Whether this is a partial clone."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003429 return self.config.GetBoolean('repo.partialclone')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003430
3431 @property
3432 def depth(self):
3433 """Partial clone depth."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003434 return self.config.GetString('repo.depth')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003435
3436 @property
3437 def clone_filter(self):
3438 """The clone filter."""
LaMont Jones4ada0432022-04-14 15:10:43 +00003439 return self.config.GetString('repo.clonefilter')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003440
3441 @property
3442 def partial_clone_exclude(self):
3443 """Partial clone exclude string"""
LaMont Jones4ada0432022-04-14 15:10:43 +00003444 return self.config.GetBoolean('repo.partialcloneexclude')
3445
3446 @property
3447 def manifest_platform(self):
3448 """The --platform argument from `repo init`."""
3449 return self.config.GetString('manifest.platform')
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003450
3451 @property
LaMont Jones9b03f152022-03-29 23:01:18 +00003452 def _platform_name(self):
3453 """Return the name of the platform."""
3454 return platform.system().lower()
3455
3456 def Sync(self, _kwargs_only=(), manifest_url='', manifest_branch=None,
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003457 standalone_manifest=False, groups='', mirror=False, reference='',
3458 dissociate=False, worktree=False, submodules=False, archive=False,
3459 partial_clone=None, depth=None, clone_filter='blob:none',
LaMont Jones9b03f152022-03-29 23:01:18 +00003460 partial_clone_exclude=None, clone_bundle=None, git_lfs=None,
3461 use_superproject=None, verbose=False, current_branch_only=False,
LaMont Jones55ee3042022-04-06 17:10:21 +00003462 git_event_log=None, platform='', manifest_name='default.xml',
3463 tags='', this_manifest_only=False, outer_manifest=True):
LaMont Jones9b03f152022-03-29 23:01:18 +00003464 """Sync the manifest and all submanifests.
3465
3466 Args:
3467 manifest_url: a string, the URL of the manifest project.
3468 manifest_branch: a string, the manifest branch to use.
3469 standalone_manifest: a boolean, whether to store the manifest as a static
3470 file.
3471 groups: a string, restricts the checkout to projects with the specified
3472 groups.
LaMont Jones9b03f152022-03-29 23:01:18 +00003473 mirror: a boolean, whether to create a mirror of the remote repository.
3474 reference: a string, location of a repo instance to use as a reference.
3475 dissociate: a boolean, whether to dissociate from reference mirrors after
3476 clone.
3477 worktree: a boolean, whether to use git-worktree to manage projects.
3478 submodules: a boolean, whether sync submodules associated with the
3479 manifest project.
3480 archive: a boolean, whether to checkout each project as an archive. See
3481 git-archive.
3482 partial_clone: a boolean, whether to perform a partial clone.
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003483 depth: an int, how deep of a shallow clone to create.
LaMont Jones9b03f152022-03-29 23:01:18 +00003484 clone_filter: a string, filter to use with partial_clone.
3485 partial_clone_exclude : a string, comma-delimeted list of project namess
3486 to exclude from partial clone.
3487 clone_bundle: a boolean, whether to enable /clone.bundle on HTTP/HTTPS.
3488 git_lfs: a boolean, whether to enable git LFS support.
3489 use_superproject: a boolean, whether to use the manifest superproject to
3490 sync projects.
3491 verbose: a boolean, whether to show all output, rather than only errors.
3492 current_branch_only: a boolean, whether to only fetch the current manifest
3493 branch from the server.
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003494 platform: a string, restrict the checkout to projects with the specified
3495 platform group.
LaMont Jones55ee3042022-04-06 17:10:21 +00003496 git_event_log: an EventLog, for git tracing.
LaMont Jones4ada0432022-04-14 15:10:43 +00003497 tags: a boolean, whether to fetch tags.
LaMont Jones409407a2022-04-05 21:21:56 +00003498 manifest_name: a string, the name of the manifest file to use.
3499 this_manifest_only: a boolean, whether to only operate on the current sub
3500 manifest.
3501 outer_manifest: a boolean, whether to start at the outermost manifest.
LaMont Jones9b03f152022-03-29 23:01:18 +00003502
3503 Returns:
3504 a boolean, whether the sync was successful.
3505 """
3506 assert _kwargs_only == (), 'Sync only accepts keyword arguments.'
3507
LaMont Jones501733c2022-04-20 16:42:32 +00003508 groups = groups or self.manifest.GetDefaultGroupsStr(with_platform=False)
LaMont Jonesa2ff20d2022-04-07 16:49:06 +00003509 platform = platform or 'auto'
LaMont Jones55ee3042022-04-06 17:10:21 +00003510 git_event_log = git_event_log or EventLog()
LaMont Jones409407a2022-04-05 21:21:56 +00003511 if outer_manifest and self.manifest.is_submanifest:
3512 # In a multi-manifest checkout, use the outer manifest unless we are told
3513 # not to.
3514 return self.client.outer_manifest.manifestProject.Sync(
3515 manifest_url=manifest_url,
3516 manifest_branch=manifest_branch,
3517 standalone_manifest=standalone_manifest,
3518 groups=groups,
3519 platform=platform,
3520 mirror=mirror,
3521 dissociate=dissociate,
3522 reference=reference,
3523 worktree=worktree,
3524 submodules=submodules,
3525 archive=archive,
3526 partial_clone=partial_clone,
3527 clone_filter=clone_filter,
3528 partial_clone_exclude=partial_clone_exclude,
3529 clone_bundle=clone_bundle,
3530 git_lfs=git_lfs,
3531 use_superproject=use_superproject,
3532 verbose=verbose,
3533 current_branch_only=current_branch_only,
3534 tags=tags,
3535 depth=depth,
LaMont Jones55ee3042022-04-06 17:10:21 +00003536 git_event_log=git_event_log,
LaMont Jones409407a2022-04-05 21:21:56 +00003537 manifest_name=manifest_name,
3538 this_manifest_only=this_manifest_only,
3539 outer_manifest=False)
3540
LaMont Jones9b03f152022-03-29 23:01:18 +00003541 # If repo has already been initialized, we take -u with the absence of
3542 # --standalone-manifest to mean "transition to a standard repo set up",
3543 # which necessitates starting fresh.
3544 # If --standalone-manifest is set, we always tear everything down and start
3545 # anew.
3546 if self.Exists:
3547 was_standalone_manifest = self.config.GetString('manifest.standalone')
3548 if was_standalone_manifest and not manifest_url:
3549 print('fatal: repo was initialized with a standlone manifest, '
3550 'cannot be re-initialized without --manifest-url/-u')
3551 return False
3552
3553 if standalone_manifest or (was_standalone_manifest and manifest_url):
3554 self.config.ClearCache()
3555 if self.gitdir and os.path.exists(self.gitdir):
3556 platform_utils.rmtree(self.gitdir)
3557 if self.worktree and os.path.exists(self.worktree):
3558 platform_utils.rmtree(self.worktree)
3559
3560 is_new = not self.Exists
3561 if is_new:
3562 if not manifest_url:
3563 print('fatal: manifest url is required.', file=sys.stderr)
3564 return False
3565
LaMont Jones409407a2022-04-05 21:21:56 +00003566 if verbose:
LaMont Jones9b03f152022-03-29 23:01:18 +00003567 print('Downloading manifest from %s' %
3568 (GitConfig.ForUser().UrlInsteadOf(manifest_url),),
3569 file=sys.stderr)
3570
3571 # The manifest project object doesn't keep track of the path on the
3572 # server where this git is located, so let's save that here.
3573 mirrored_manifest_git = None
3574 if reference:
3575 manifest_git_path = urllib.parse.urlparse(manifest_url).path[1:]
3576 mirrored_manifest_git = os.path.join(reference, manifest_git_path)
3577 if not mirrored_manifest_git.endswith(".git"):
3578 mirrored_manifest_git += ".git"
3579 if not os.path.exists(mirrored_manifest_git):
3580 mirrored_manifest_git = os.path.join(reference,
3581 '.repo/manifests.git')
3582
3583 self._InitGitDir(mirror_git=mirrored_manifest_git)
3584
3585 # If standalone_manifest is set, mark the project as "standalone" -- we'll
3586 # still do much of the manifests.git set up, but will avoid actual syncs to
3587 # a remote.
3588 if standalone_manifest:
3589 self.config.SetString('manifest.standalone', manifest_url)
3590 elif not manifest_url and not manifest_branch:
3591 # If -u is set and --standalone-manifest is not, then we're not in
3592 # standalone mode. Otherwise, use config to infer what we were in the last
3593 # init.
3594 standalone_manifest = bool(self.config.GetString('manifest.standalone'))
3595 if not standalone_manifest:
3596 self.config.SetString('manifest.standalone', None)
3597
3598 self._ConfigureDepth(depth)
3599
3600 # Set the remote URL before the remote branch as we might need it below.
3601 if manifest_url:
3602 r = self.GetRemote(self.remote.name)
3603 r.url = manifest_url
3604 r.ResetFetch()
3605 r.Save()
3606
3607 if not standalone_manifest:
3608 if manifest_branch:
3609 if manifest_branch == 'HEAD':
3610 manifest_branch = self.ResolveRemoteHead()
3611 if manifest_branch is None:
3612 print('fatal: unable to resolve HEAD', file=sys.stderr)
3613 return False
3614 self.revisionExpr = manifest_branch
3615 else:
3616 if is_new:
3617 default_branch = self.ResolveRemoteHead()
3618 if default_branch is None:
3619 # If the remote doesn't have HEAD configured, default to master.
3620 default_branch = 'refs/heads/master'
3621 self.revisionExpr = default_branch
3622 else:
3623 self.PreSync()
3624
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003625 groups = re.split(r'[,\s]+', groups or '')
LaMont Jones9b03f152022-03-29 23:01:18 +00003626 all_platforms = ['linux', 'darwin', 'windows']
3627 platformize = lambda x: 'platform-' + x
3628 if platform == 'auto':
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003629 if not mirror and not self.mirror:
LaMont Jones9b03f152022-03-29 23:01:18 +00003630 groups.append(platformize(self._platform_name))
3631 elif platform == 'all':
3632 groups.extend(map(platformize, all_platforms))
3633 elif platform in all_platforms:
3634 groups.append(platformize(platform))
3635 elif platform != 'none':
3636 print('fatal: invalid platform flag', file=sys.stderr)
3637 return False
LaMont Jones4ada0432022-04-14 15:10:43 +00003638 self.config.SetString('manifest.platform', platform)
LaMont Jones9b03f152022-03-29 23:01:18 +00003639
3640 groups = [x for x in groups if x]
3641 groupstr = ','.join(groups)
3642 if platform == 'auto' and groupstr == self.manifest.GetDefaultGroupsStr():
3643 groupstr = None
3644 self.config.SetString('manifest.groups', groupstr)
3645
3646 if reference:
3647 self.config.SetString('repo.reference', reference)
3648
3649 if dissociate:
3650 self.config.SetBoolean('repo.dissociate', dissociate)
3651
3652 if worktree:
3653 if mirror:
3654 print('fatal: --mirror and --worktree are incompatible',
3655 file=sys.stderr)
3656 return False
3657 if submodules:
3658 print('fatal: --submodules and --worktree are incompatible',
3659 file=sys.stderr)
3660 return False
3661 self.config.SetBoolean('repo.worktree', worktree)
3662 if is_new:
3663 self.use_git_worktrees = True
3664 print('warning: --worktree is experimental!', file=sys.stderr)
3665
3666 if archive:
3667 if is_new:
3668 self.config.SetBoolean('repo.archive', archive)
3669 else:
3670 print('fatal: --archive is only supported when initializing a new '
3671 'workspace.', file=sys.stderr)
3672 print('Either delete the .repo folder in this workspace, or initialize '
3673 'in another location.', file=sys.stderr)
3674 return False
3675
3676 if mirror:
3677 if is_new:
3678 self.config.SetBoolean('repo.mirror', mirror)
3679 else:
3680 print('fatal: --mirror is only supported when initializing a new '
3681 'workspace.', file=sys.stderr)
3682 print('Either delete the .repo folder in this workspace, or initialize '
3683 'in another location.', file=sys.stderr)
3684 return False
3685
3686 if partial_clone is not None:
3687 if mirror:
3688 print('fatal: --mirror and --partial-clone are mutually exclusive',
3689 file=sys.stderr)
3690 return False
3691 self.config.SetBoolean('repo.partialclone', partial_clone)
3692 if clone_filter:
3693 self.config.SetString('repo.clonefilter', clone_filter)
LaMont Jones55ee3042022-04-06 17:10:21 +00003694 elif self.partial_clone:
LaMont Jonesd82be3e2022-04-05 19:30:46 +00003695 clone_filter = self.clone_filter
LaMont Jones9b03f152022-03-29 23:01:18 +00003696 else:
3697 clone_filter = None
3698
3699 if partial_clone_exclude is not None:
3700 self.config.SetString('repo.partialcloneexclude', partial_clone_exclude)
3701
3702 if clone_bundle is None:
3703 clone_bundle = False if partial_clone else True
3704 else:
3705 self.config.SetBoolean('repo.clonebundle', clone_bundle)
3706
3707 if submodules:
3708 self.config.SetBoolean('repo.submodules', submodules)
3709
3710 if git_lfs is not None:
3711 if git_lfs:
3712 git_require((2, 17, 0), fail=True, msg='Git LFS support')
3713
3714 self.config.SetBoolean('repo.git-lfs', git_lfs)
3715 if not is_new:
3716 print('warning: Changing --git-lfs settings will only affect new project checkouts.\n'
3717 ' Existing projects will require manual updates.\n', file=sys.stderr)
3718
3719 if use_superproject is not None:
3720 self.config.SetBoolean('repo.superproject', use_superproject)
3721
LaMont Jones0165e202022-04-27 17:34:42 +00003722 if not standalone_manifest:
3723 if not self.Sync_NetworkHalf(
3724 is_new=is_new, quiet=not verbose, verbose=verbose,
3725 clone_bundle=clone_bundle, current_branch_only=current_branch_only,
3726 tags=tags, submodules=submodules, clone_filter=clone_filter,
3727 partial_clone_exclude=self.manifest.PartialCloneExclude):
3728 r = self.GetRemote(self.remote.name)
3729 print('fatal: cannot obtain manifest %s' % r.url, file=sys.stderr)
LaMont Jones9b03f152022-03-29 23:01:18 +00003730
LaMont Jones0165e202022-04-27 17:34:42 +00003731 # Better delete the manifest git dir if we created it; otherwise next
3732 # time (when user fixes problems) we won't go through the "is_new" logic.
3733 if is_new:
3734 platform_utils.rmtree(self.gitdir)
LaMont Jones9b03f152022-03-29 23:01:18 +00003735 return False
3736
LaMont Jones0165e202022-04-27 17:34:42 +00003737 if manifest_branch:
3738 self.MetaBranchSwitch(submodules=submodules)
3739
3740 syncbuf = SyncBuffer(self.config)
3741 self.Sync_LocalHalf(syncbuf, submodules=submodules)
3742 syncbuf.Finish()
3743
3744 if is_new or self.CurrentBranch is None:
3745 if not self.StartBranch('default'):
3746 print('fatal: cannot create default in manifest', file=sys.stderr)
3747 return False
3748
3749 if not manifest_name:
3750 print('fatal: manifest name (-m) is required.', file=sys.stderr)
3751 return False
3752
3753 elif is_new:
3754 # This is a new standalone manifest.
3755 manifest_name = 'default.xml'
3756 manifest_data = fetch.fetch_file(manifest_url, verbose=verbose)
3757 dest = os.path.join(self.worktree, manifest_name)
3758 os.makedirs(os.path.dirname(dest), exist_ok=True)
3759 with open(dest, 'wb') as f:
3760 f.write(manifest_data)
LaMont Jones409407a2022-04-05 21:21:56 +00003761
3762 try:
3763 self.manifest.Link(manifest_name)
3764 except ManifestParseError as e:
3765 print("fatal: manifest '%s' not available" % manifest_name,
3766 file=sys.stderr)
3767 print('fatal: %s' % str(e), file=sys.stderr)
3768 return False
3769
LaMont Jones55ee3042022-04-06 17:10:21 +00003770 if not this_manifest_only:
3771 for submanifest in self.manifest.submanifests.values():
LaMont Jonesb90a4222022-04-14 15:00:09 +00003772 spec = submanifest.ToSubmanifestSpec()
LaMont Jones55ee3042022-04-06 17:10:21 +00003773 submanifest.repo_client.manifestProject.Sync(
3774 manifest_url=spec.manifestUrl,
3775 manifest_branch=spec.revision,
3776 standalone_manifest=standalone_manifest,
3777 groups=self.manifest_groups,
3778 platform=platform,
3779 mirror=mirror,
3780 dissociate=dissociate,
3781 reference=reference,
3782 worktree=worktree,
3783 submodules=submodules,
3784 archive=archive,
3785 partial_clone=partial_clone,
3786 clone_filter=clone_filter,
3787 partial_clone_exclude=partial_clone_exclude,
3788 clone_bundle=clone_bundle,
3789 git_lfs=git_lfs,
3790 use_superproject=use_superproject,
3791 verbose=verbose,
3792 current_branch_only=current_branch_only,
3793 tags=tags,
3794 depth=depth,
3795 git_event_log=git_event_log,
3796 manifest_name=spec.manifestName,
3797 this_manifest_only=False,
3798 outer_manifest=False,
3799 )
LaMont Jones409407a2022-04-05 21:21:56 +00003800
LaMont Jones0ddb6772022-05-20 09:11:54 +00003801 # Lastly, if the manifest has a <superproject> then have the superproject
3802 # sync it if it will be used.
3803 if self.manifest.superproject:
3804 sync_result = self.manifest.superproject.Sync(git_event_log)
LaMont Jonesa2ff20d2022-04-07 16:49:06 +00003805 if not sync_result.success:
3806 print('warning: git update of superproject for '
3807 f'{self.manifest.path_prefix} failed, repo sync will not use '
3808 'superproject to fetch source; while this error is not fatal, '
3809 'and you can continue to run repo sync, please run repo init '
3810 'with the --no-use-superproject option to stop seeing this '
3811 'warning', file=sys.stderr)
3812 if sync_result.fatal and use_superproject is not None:
3813 return False
LaMont Jones409407a2022-04-05 21:21:56 +00003814
LaMont Jones9b03f152022-03-29 23:01:18 +00003815 return True
3816
3817 def _ConfigureDepth(self, depth):
3818 """Configure the depth we'll sync down.
3819
3820 Args:
3821 depth: an int, how deep of a partial clone to create.
3822 """
3823 # Opt.depth will be non-None if user actually passed --depth to repo init.
3824 if depth is not None:
3825 if depth > 0:
3826 # Positive values will set the depth.
3827 depth = str(depth)
3828 else:
3829 # Negative numbers will clear the depth; passing None to SetString
3830 # will do that.
3831 depth = None
3832
3833 # We store the depth in the main manifest project.
3834 self.config.SetString('repo.depth', depth)