blob: 634d88c5daf243e143239f6c9a360a88b8f6a056 [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
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070019import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import re
21import shutil
22import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070023import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020025import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080026import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.parse
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070029
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070031from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070032from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
33 ID_RE
Remy Bohmer16c13282020-09-10 10:38:04 +020034from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040035from error import ManifestInvalidRevisionError, ManifestInvalidPathError
Conley Owens75ee0572012-11-15 17:33:11 -080036from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070037import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040038import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040039from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050041from 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 -070042
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070043
George Engelbrecht9bc283e2020-04-02 12:36:09 -060044# Maximum sleep time allowed during retries.
45MAXIMUM_RETRY_SLEEP_SEC = 3600.0
46# +-10% random jitter is added to each Fetches retry sleep duration.
47RETRY_JITTER_PERCENT = 0.1
48
49
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070050def _lwrite(path, content):
51 lock = '%s.lock' % path
52
Remy Bohmer169b0212020-11-21 10:57:52 +010053 # Maintain Unix line endings on all OS's to match git behavior.
54 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056
57 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070058 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080060 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 raise
62
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070063
Shawn O. Pearce48244782009-04-16 08:25:57 -070064def _error(fmt, *args):
65 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070066 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070067
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070068
David Pursehousef33929d2015-08-24 14:39:14 +090069def _warn(fmt, *args):
70 msg = fmt % args
71 print('warn: %s' % msg, file=sys.stderr)
72
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070073
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070074def not_rev(r):
75 return '^' + r
76
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080078def sq(r):
79 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080080
David Pursehouse819827a2020-02-12 15:20:19 +090081
Jonathan Nieder93719792015-03-17 11:29:58 -070082_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
84
Jonathan Nieder93719792015-03-17 11:29:58 -070085def _ProjectHooks():
86 """List the hooks present in the 'hooks' directory.
87
88 These hooks are project hooks and are copied to the '.git/hooks' directory
89 of all subprojects.
90
91 This function caches the list of hooks (based on the contents of the
92 'repo/hooks' directory) on the first call.
93
94 Returns:
95 A list of absolute paths to all of the files in the hooks directory.
96 """
97 global _project_hook_list
98 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -070099 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700100 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700101 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700102 return _project_hook_list
103
104
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700105class DownloadedChange(object):
106 _commit_cache = None
107
108 def __init__(self, project, base, change_id, ps_id, commit):
109 self.project = project
110 self.base = base
111 self.change_id = change_id
112 self.ps_id = ps_id
113 self.commit = commit
114
115 @property
116 def commits(self):
117 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700118 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
119 '--abbrev-commit',
120 '--pretty=oneline',
121 '--reverse',
122 '--date-order',
123 not_rev(self.base),
124 self.commit,
125 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700126 return self._commit_cache
127
128
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700129class ReviewableBranch(object):
130 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400131 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700132
133 def __init__(self, project, branch, base):
134 self.project = project
135 self.branch = branch
136 self.base = base
137
138 @property
139 def name(self):
140 return self.branch.name
141
142 @property
143 def commits(self):
144 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400145 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
146 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
147 try:
148 self._commit_cache = self.project.bare_git.rev_list(*args)
149 except GitError:
150 # We weren't able to probe the commits for this branch. Was it tracking
151 # a branch that no longer exists? If so, return no commits. Otherwise,
152 # rethrow the error as we don't know what's going on.
153 if self.base_exists:
154 raise
155
156 self._commit_cache = []
157
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mike Frysinger6da17752019-09-11 18:43:17 -0400176 @property
177 def base_exists(self):
178 """Whether the branch we're tracking exists.
179
180 Normally it should, but sometimes branches we track can get deleted.
181 """
182 if self._base_exists is None:
183 try:
184 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
185 # If we're still here, the base branch exists.
186 self._base_exists = True
187 except GitError:
188 # If we failed to verify, the base branch doesn't exist.
189 self._base_exists = False
190
191 return self._base_exists
192
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700193 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500194 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700195 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500196 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500197 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200198 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700199 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200200 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200201 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800202 validate_certs=True,
203 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500204 self.project.UploadForReview(branch=self.name,
205 people=people,
206 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700207 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500208 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500209 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200210 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700211 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200213 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800214 validate_certs=validate_certs,
215 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700217 def GetPublishedRefs(self):
218 refs = {}
219 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700220 self.branch.remote.SshReviewUrl(self.project.UserEmail),
221 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700222 for line in output.split('\n'):
223 try:
224 (sha, ref) = line.split()
225 refs[sha] = ref
226 except ValueError:
227 pass
228
229 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500235 super().__init__(config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100236 self.project = self.printer('header', attr='bold')
237 self.branch = self.printer('header', attr='bold')
238 self.nobranch = self.printer('nobranch', fg='red')
239 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Anthony King7bdac712014-07-16 12:56:40 +0100241 self.added = self.printer('added', fg='green')
242 self.changed = self.printer('changed', fg='red')
243 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244
245
246class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700247
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500249 super().__init__(config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100250 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400251 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700253
Jack Neus6ea0cae2021-07-20 20:52:33 +0000254class Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700255
James W. Mills24c13082012-04-12 15:04:13 -0500256 def __init__(self, name, value, keep):
257 self.name = name
258 self.value = value
259 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260
Jack Neus6ea0cae2021-07-20 20:52:33 +0000261 def __eq__(self, other):
262 if not isinstance(other, Annotation):
263 return False
264 return self.__dict__ == other.__dict__
265
266 def __lt__(self, other):
267 # This exists just so that lists of Annotation objects can be sorted, for
268 # use in comparisons.
269 if not isinstance(other, Annotation):
270 raise ValueError('comparison is not between two Annotation objects')
271 if self.name == other.name:
272 if self.value == other.value:
273 return self.keep < other.keep
274 return self.value < other.value
275 return self.name < other.name
276
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700277
Mike Frysingere6a202f2019-08-02 15:57:57 -0400278def _SafeExpandPath(base, subpath, skipfinal=False):
279 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700280
Mike Frysingere6a202f2019-08-02 15:57:57 -0400281 We make sure no intermediate symlinks are traversed, and that the final path
282 is not a special file (e.g. not a socket or fifo).
283
284 NB: We rely on a number of paths already being filtered out while parsing the
285 manifest. See the validation logic in manifest_xml.py for more details.
286 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500287 # Split up the path by its components. We can't use os.path.sep exclusively
288 # as some platforms (like Windows) will convert / to \ and that bypasses all
289 # our constructed logic here. Especially since manifest authors only use
290 # / in their paths.
291 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
292 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400293 if skipfinal:
294 # Whether the caller handles the final component itself.
295 finalpart = components.pop()
296
297 path = base
298 for part in components:
299 if part in {'.', '..'}:
300 raise ManifestInvalidPathError(
301 '%s: "%s" not allowed in paths' % (subpath, part))
302
303 path = os.path.join(path, part)
304 if platform_utils.islink(path):
305 raise ManifestInvalidPathError(
306 '%s: traversing symlinks not allow' % (path,))
307
308 if os.path.exists(path):
309 if not os.path.isfile(path) and not platform_utils.isdir(path):
310 raise ManifestInvalidPathError(
311 '%s: only regular files & directories allowed' % (path,))
312
313 if skipfinal:
314 path = os.path.join(path, finalpart)
315
316 return path
317
318
319class _CopyFile(object):
320 """Container for <copyfile> manifest element."""
321
322 def __init__(self, git_worktree, src, topdir, dest):
323 """Register a <copyfile> request.
324
325 Args:
326 git_worktree: Absolute path to the git project checkout.
327 src: Relative path under |git_worktree| of file to read.
328 topdir: Absolute path to the top of the repo client checkout.
329 dest: Relative path under |topdir| of file to write.
330 """
331 self.git_worktree = git_worktree
332 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333 self.src = src
334 self.dest = dest
335
336 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400337 src = _SafeExpandPath(self.git_worktree, self.src)
338 dest = _SafeExpandPath(self.topdir, self.dest)
339
340 if platform_utils.isdir(src):
341 raise ManifestInvalidPathError(
342 '%s: copying from directory not supported' % (self.src,))
343 if platform_utils.isdir(dest):
344 raise ManifestInvalidPathError(
345 '%s: copying to directory not allowed' % (self.dest,))
346
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347 # copy file if it does not exist or is out of date
348 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
349 try:
350 # remove existing file first, since it might be read-only
351 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800352 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400353 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200354 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700355 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200356 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700357 shutil.copy(src, dest)
358 # make the file read-only
359 mode = os.stat(dest)[stat.ST_MODE]
360 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
361 os.chmod(dest, mode)
362 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700363 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700364
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700365
Anthony King7bdac712014-07-16 12:56:40 +0100366class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400367 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700368
Mike Frysingere6a202f2019-08-02 15:57:57 -0400369 def __init__(self, git_worktree, src, topdir, dest):
370 """Register a <linkfile> request.
371
372 Args:
373 git_worktree: Absolute path to the git project checkout.
374 src: Target of symlink relative to path under |git_worktree|.
375 topdir: Absolute path to the top of the repo client checkout.
376 dest: Relative path under |topdir| of symlink to create.
377 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700378 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400379 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500380 self.src = src
381 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500382
Wink Saville4c426ef2015-06-03 08:05:17 -0700383 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500384 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700385 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500386 try:
387 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800388 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800389 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500390 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700391 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700392 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500393 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700394 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500395 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700396 _error('Cannot link file %s to %s', relSrc, absDest)
397
398 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400399 """Link the self.src & self.dest paths.
400
401 Handles wild cards on the src linking all of the files in the source in to
402 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700403 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500404 # Some people use src="." to create stable links to projects. Lets allow
405 # that but reject all other uses of "." to keep things simple.
406 if self.src == '.':
407 src = self.git_worktree
408 else:
409 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400410
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300411 if not glob.has_magic(src):
412 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400413 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
414 # dest & src are absolute paths at this point. Make sure the target of
415 # the symlink is relative in the context of the repo client checkout.
416 relpath = os.path.relpath(src, os.path.dirname(dest))
417 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700418 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400419 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300420 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400421 if os.path.exists(dest) and not platform_utils.isdir(dest):
422 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700423 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400424 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700425 # Create a releative path from source dir to destination dir
426 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400427 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700428
429 # Get the source file name
430 srcFile = os.path.basename(absSrcFile)
431
432 # Now form the final full paths to srcFile. They will be
433 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400434 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700435 relSrc = os.path.join(relSrcDir, srcFile)
436 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500437
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700438
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700439class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700440
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700441 def __init__(self,
442 name,
Anthony King7bdac712014-07-16 12:56:40 +0100443 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700444 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100445 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700446 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700447 orig_name=None,
448 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700449 self.name = name
450 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700451 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700452 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100453 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700454 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700455 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456
Ian Kasprzak0286e312021-02-05 10:06:18 -0800457
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700458class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600459 # These objects can be shared between several working trees.
460 shareable_files = ['description', 'info']
461 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
462 # These objects can only be used by a single working tree.
463 working_tree_files = ['config', 'packed-refs', 'shallow']
464 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700465
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700466 def __init__(self,
467 manifest,
468 name,
469 remote,
470 gitdir,
David James8d201162013-10-11 17:03:19 -0700471 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700472 worktree,
473 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700474 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800475 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100476 rebase=True,
477 groups=None,
478 sync_c=False,
479 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900480 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100481 clone_depth=None,
482 upstream=None,
483 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500484 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100485 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900486 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700487 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600488 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700489 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800490 """Init a Project object.
491
492 Args:
493 manifest: The XmlManifest object.
494 name: The `name` attribute of manifest.xml's project element.
495 remote: RemoteSpec object specifying its remote's properties.
496 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700497 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800498 worktree: Absolute path of git working tree.
499 relpath: Relative path of git working tree to repo's top directory.
500 revisionExpr: The `revision` attribute of manifest.xml's project element.
501 revisionId: git commit id for checking out.
502 rebase: The `rebase` attribute of manifest.xml's project element.
503 groups: The `groups` attribute of manifest.xml's project element.
504 sync_c: The `sync-c` attribute of manifest.xml's project element.
505 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900506 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800507 upstream: The `upstream` attribute of manifest.xml's project element.
508 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500509 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800510 is_derived: False if the project was explicitly defined in the manifest;
511 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400512 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900513 optimized_fetch: If True, when a project is set to a sha1 revision, only
514 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600515 retry_fetches: Retry remote fetches n times upon receiving transient error
516 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700517 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800518 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400519 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520 self.name = name
521 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800522 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700523 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800524 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700525 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800526 else:
527 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700528 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700529 self.revisionExpr = revisionExpr
530
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700531 if revisionId is None \
532 and revisionExpr \
533 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700534 self.revisionId = revisionExpr
535 else:
536 self.revisionId = revisionId
537
Mike Pontillod3153822012-02-28 11:53:24 -0800538 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700539 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700540 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800541 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900542 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900543 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700544 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800545 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500546 # NB: Do not use this setting in __init__ to change behavior so that the
547 # manifest.git checkout can inspect & change it after instantiating. See
548 # the XmlManifest init code for more info.
549 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800550 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900551 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600552 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800553 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800554
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700556 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500557 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500558 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700559 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400560 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700561
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800562 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700563 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800564 else:
565 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700566 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700567 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700568 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400569 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700570 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700571
Doug Anderson37282b42011-03-04 11:54:18 -0800572 # This will be filled in if a project is later identified to be the
573 # project containing repo hooks.
574 self.enabled_repo_hooks = []
575
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700576 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800577 def Derived(self):
578 return self.is_derived
579
580 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700581 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700582 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700583
584 @property
585 def CurrentBranch(self):
586 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400587
588 The branch name omits the 'refs/heads/' prefix.
589 None is returned if the project is on a detached HEAD, or if the work_git is
590 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400592 try:
593 b = self.work_git.GetHead()
594 except NoManifestException:
595 # If the local checkout is in a bad state, don't barf. Let the callers
596 # process this like the head is unreadable.
597 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700598 if b.startswith(R_HEADS):
599 return b[len(R_HEADS):]
600 return None
601
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700602 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500603 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
604 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
605 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200606
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700607 def IsDirty(self, consider_untracked=True):
608 """Is the working directory modified in some way?
609 """
610 self.work_git.update_index('-q',
611 '--unmerged',
612 '--ignore-missing',
613 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900614 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700615 return True
616 if self.work_git.DiffZ('diff-files'):
617 return True
618 if consider_untracked and self.work_git.LsOthers():
619 return True
620 return False
621
622 _userident_name = None
623 _userident_email = None
624
625 @property
626 def UserName(self):
627 """Obtain the user's personal name.
628 """
629 if self._userident_name is None:
630 self._LoadUserIdentity()
631 return self._userident_name
632
633 @property
634 def UserEmail(self):
635 """Obtain the user's email address. This is very likely
636 to be their Gerrit login.
637 """
638 if self._userident_email is None:
639 self._LoadUserIdentity()
640 return self._userident_email
641
642 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900643 u = self.bare_git.var('GIT_COMMITTER_IDENT')
644 m = re.compile("^(.*) <([^>]*)> ").match(u)
645 if m:
646 self._userident_name = m.group(1)
647 self._userident_email = m.group(2)
648 else:
649 self._userident_name = ''
650 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700651
652 def GetRemote(self, name):
653 """Get the configuration for a single remote.
654 """
655 return self.config.GetRemote(name)
656
657 def GetBranch(self, name):
658 """Get the configuration for a single branch.
659 """
660 return self.config.GetBranch(name)
661
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700662 def GetBranches(self):
663 """Get all existing local branches.
664 """
665 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900666 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700667 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700668
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530669 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700670 if name.startswith(R_HEADS):
671 name = name[len(R_HEADS):]
672 b = self.GetBranch(name)
673 b.current = name == current
674 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900675 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700676 heads[name] = b
677
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530678 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700679 if name.startswith(R_PUB):
680 name = name[len(R_PUB):]
681 b = heads.get(name)
682 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900683 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700684
685 return heads
686
Colin Cross5acde752012-03-28 20:15:45 -0700687 def MatchesGroups(self, manifest_groups):
688 """Returns true if the manifest groups specified at init should cause
689 this project to be synced.
690 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700691 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700692
693 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700694 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700695 manifest_groups: "-group1,group2"
696 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500697
698 The special manifest group "default" will match any project that
699 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700700 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500701 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700702 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700703 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500704 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700705
Conley Owens971de8e2012-04-16 10:36:08 -0700706 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700707 for group in expanded_manifest_groups:
708 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700709 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700710 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700711 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700712
Conley Owens971de8e2012-04-16 10:36:08 -0700713 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700714
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700715# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700716 def UncommitedFiles(self, get_all=True):
717 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700718
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700719 Args:
720 get_all: a boolean, if True - get information about all different
721 uncommitted files. If False - return as soon as any kind of
722 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500723 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500725 self.work_git.update_index('-q',
726 '--unmerged',
727 '--ignore-missing',
728 '--refresh')
729 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700730 details.append("rebase in progress")
731 if not get_all:
732 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500733
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
735 if changes:
736 details.extend(changes)
737 if not get_all:
738 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500739
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700740 changes = self.work_git.DiffZ('diff-files').keys()
741 if changes:
742 details.extend(changes)
743 if not get_all:
744 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500745
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700746 changes = self.work_git.LsOthers()
747 if changes:
748 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500749
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700750 return details
751
752 def HasChanges(self):
753 """Returns true if there are uncommitted changes.
754 """
755 if self.UncommitedFiles(get_all=False):
756 return True
757 else:
758 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500759
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600760 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700761 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200762
763 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200764 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600765 quiet: If True then only print the project name. Do not print
766 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700767 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700768 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700769 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200770 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700771 print(file=output_redir)
772 print('project %s/' % self.relpath, file=output_redir)
773 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700774 return
775
776 self.work_git.update_index('-q',
777 '--unmerged',
778 '--ignore-missing',
779 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700780 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700781 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
782 df = self.work_git.DiffZ('diff-files')
783 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100784 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700785 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700786
787 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700788 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200789 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700790 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600792 if quiet:
793 out.nl()
794 return 'DIRTY'
795
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700796 branch = self.CurrentBranch
797 if branch is None:
798 out.nobranch('(*** NO BRANCH ***)')
799 else:
800 out.branch('branch %s', branch)
801 out.nl()
802
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700803 if rb:
804 out.important('prior sync failed; rebase still in progress')
805 out.nl()
806
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700807 paths = list()
808 paths.extend(di.keys())
809 paths.extend(df.keys())
810 paths.extend(do)
811
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530812 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900813 try:
814 i = di[p]
815 except KeyError:
816 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900818 try:
819 f = df[p]
820 except KeyError:
821 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200822
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900823 if i:
824 i_status = i.status.upper()
825 else:
826 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700827
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900828 if f:
829 f_status = f.status.lower()
830 else:
831 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832
833 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800834 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700835 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 else:
837 line = ' %s%s\t%s' % (i_status, f_status, p)
838
839 if i and not f:
840 out.added('%s', line)
841 elif (i and f) or (not i and f):
842 out.changed('%s', line)
843 elif not i and not f:
844 out.untracked('%s', line)
845 else:
846 out.write('%s', line)
847 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200848
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700849 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700850
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500851 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700852 """Prints the status of the repository to stdout.
853 """
854 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500855 if output_redir:
856 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 cmd = ['diff']
858 if out.is_on:
859 cmd.append('--color')
860 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300861 if absolute_paths:
862 cmd.append('--src-prefix=a/%s/' % self.relpath)
863 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400865 try:
866 p = GitCommand(self,
867 cmd,
868 capture_stdout=True,
869 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500870 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400871 except GitError as e:
872 out.nl()
873 out.project('project %s/' % self.relpath)
874 out.nl()
875 out.fail('%s', str(e))
876 out.nl()
877 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500878 if p.stdout:
879 out.nl()
880 out.project('project %s/' % self.relpath)
881 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500882 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400883 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700885# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900886 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887 """Was the branch published (uploaded) for code review?
888 If so, returns the SHA-1 hash of the last published
889 state for the branch.
890 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700891 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900892 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700893 try:
894 return self.bare_git.rev_parse(key)
895 except GitError:
896 return None
897 else:
898 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900899 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700900 except KeyError:
901 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700902
David Pursehouse8a68ff92012-09-24 12:15:13 +0900903 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904 """Prunes any stale published refs.
905 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900906 if all_refs is None:
907 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908 heads = set()
909 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530910 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911 if name.startswith(R_HEADS):
912 heads.add(name)
913 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900914 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700915
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530916 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917 n = name[len(R_PUB):]
918 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900919 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700921 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 """List any branches which can be uploaded for review.
923 """
924 heads = {}
925 pubed = {}
926
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530927 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900929 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900931 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932
933 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530934 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900935 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700937 if selected_branch and branch != selected_branch:
938 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800940 rb = self.GetUploadableBranch(branch)
941 if rb:
942 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700943 return ready
944
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800945 def GetUploadableBranch(self, branch_name):
946 """Get a single uploadable branch, or None.
947 """
948 branch = self.GetBranch(branch_name)
949 base = branch.LocalMerge
950 if branch.LocalMerge:
951 rb = ReviewableBranch(self, branch, base)
952 if rb.commits:
953 return rb
954 return None
955
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700956 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100957 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500958 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700959 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500960 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500961 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200962 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700963 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200964 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200965 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800966 validate_certs=True,
967 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 """Uploads the named branch for code review.
969 """
970 if branch is None:
971 branch = self.CurrentBranch
972 if branch is None:
973 raise GitError('not currently on a branch')
974
975 branch = self.GetBranch(branch)
976 if not branch.LocalMerge:
977 raise GitError('branch %s does not track a remote' % branch.name)
978 if not branch.remote.review:
979 raise GitError('remote %s has no review url' % branch.remote.name)
980
Bryan Jacobsf609f912013-05-06 13:36:24 -0400981 if dest_branch is None:
982 dest_branch = self.dest_branch
983 if dest_branch is None:
984 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700985 if not dest_branch.startswith(R_HEADS):
986 dest_branch = R_HEADS + dest_branch
987
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800988 if not branch.remote.projectname:
989 branch.remote.projectname = self.name
990 branch.remote.Save()
991
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200992 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800993 if url is None:
994 raise UploadError('review not configured')
995 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500996 if dryrun:
997 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800998
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800999 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001000 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001001
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001002 for push_option in (push_options or []):
1003 cmd.append('-o')
1004 cmd.append(push_option)
1005
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001006 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001007
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001008 if dest_branch.startswith(R_HEADS):
1009 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001010
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001011 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001012 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001013 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001014 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001015 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001016 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001017
David Pursehousef25a3702018-11-14 19:01:22 -08001018 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001019 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001020 if notify:
1021 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001022 if private:
1023 opts += ['private']
1024 if wip:
1025 opts += ['wip']
1026 if opts:
1027 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001028 cmd.append(ref_spec)
1029
Anthony King7bdac712014-07-16 12:56:40 +01001030 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001031 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032
Mike Frysingerd7f86832020-11-19 19:18:46 -05001033 if not dryrun:
1034 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1035 self.bare_git.UpdateRef(R_PUB + branch.name,
1036 R_HEADS + branch.name,
1037 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001039# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001040 def _ExtractArchive(self, tarpath, path=None):
1041 """Extract the given tar on its current location
1042
1043 Args:
1044 - tarpath: The path to the actual tar file
1045
1046 """
1047 try:
1048 with tarfile.open(tarpath, 'r') as tar:
1049 tar.extractall(path=path)
1050 return True
1051 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001052 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001053 return False
1054
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001055 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001056 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001057 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001058 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001059 is_new=None,
Mike Frysinger73561142021-05-03 01:10:09 -04001060 current_branch_only=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001061 force_sync=False,
1062 clone_bundle=True,
Mike Frysingerd68ed632021-05-03 01:21:35 -04001063 tags=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001064 archive=False,
1065 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001066 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001067 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001068 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001069 ssh_proxy=None,
Raman Tennetif32f2432021-04-12 20:57:25 -07001070 clone_filter=None,
Raman Tenneticd89ec12021-04-22 09:18:14 -07001071 partial_clone_exclude=set()):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 """Perform only the network IO portion of the sync process.
1073 Local working directory/branch state is not affected.
1074 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001075 if archive and not isinstance(self, MetaProject):
1076 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001077 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001078 return False
1079
1080 name = self.relpath.replace('\\', '/')
1081 name = name.replace('/', '_')
1082 tarpath = '%s.tar' % name
1083 topdir = self.manifest.topdir
1084
1085 try:
1086 self._FetchArchive(tarpath, cwd=topdir)
1087 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001088 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001089 return False
1090
1091 # From now on, we only need absolute tarpath
1092 tarpath = os.path.join(topdir, tarpath)
1093
1094 if not self._ExtractArchive(tarpath, path=topdir):
1095 return False
1096 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001097 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001098 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001099 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001100 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001101 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001102
1103 # If the shared object dir already exists, don't try to rebootstrap with a
1104 # clone bundle download. We should have the majority of objects already.
1105 if clone_bundle and os.path.exists(self.objdir):
1106 clone_bundle = False
1107
Raman Tennetif32f2432021-04-12 20:57:25 -07001108 if self.name in partial_clone_exclude:
1109 clone_bundle = True
1110 clone_filter = None
1111
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001112 if is_new is None:
1113 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001114 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001115 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001116 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001117 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001118 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001119
1120 if is_new:
1121 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1122 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001123 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001124 # This works for both absolute and relative alternate directories.
1125 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001126 except IOError:
1127 alt_dir = None
1128 else:
1129 alt_dir = None
1130
Mike Frysingere50b6a72020-02-19 01:45:48 -05001131 if (clone_bundle
1132 and alt_dir is None
1133 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001134 is_new = False
1135
Mike Frysinger73561142021-05-03 01:10:09 -04001136 if current_branch_only is None:
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001137 if self.sync_c:
1138 current_branch_only = True
1139 elif not self.manifest._loaded:
1140 # Manifest cannot check defaults until it syncs.
1141 current_branch_only = False
1142 elif self.manifest.default.sync_c:
1143 current_branch_only = True
1144
Mike Frysingerd68ed632021-05-03 01:21:35 -04001145 if tags is None:
1146 tags = self.sync_tags
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001147
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001148 if self.clone_depth:
1149 depth = self.clone_depth
1150 else:
1151 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1152
Mike Frysinger521d01b2020-02-17 01:51:49 -05001153 # See if we can skip the network fetch entirely.
1154 if not (optimized_fetch and
1155 (ID_RE.match(self.revisionExpr) and
1156 self._CheckForImmutableRevision())):
1157 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001158 initial=is_new,
1159 quiet=quiet, verbose=verbose, output_redir=output_redir,
1160 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001161 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001162 submodules=submodules, force_sync=force_sync,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001163 ssh_proxy=ssh_proxy,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001164 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001165 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001166
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001167 mp = self.manifest.manifestProject
1168 dissociate = mp.config.GetBoolean('repo.dissociate')
1169 if dissociate:
1170 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1171 if os.path.exists(alternates_file):
1172 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001173 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1174 merge_output=bool(output_redir))
1175 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001176 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001177 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001178 return False
1179 platform_utils.remove(alternates_file)
1180
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001181 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001182 self._InitMRef()
1183 else:
1184 self._InitMirrorHead()
Mike Frysinger9d96f582021-09-28 11:27:24 -04001185 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
1186 missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001188
1189 def PostRepoUpgrade(self):
1190 self._InitHooks()
1191
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001192 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001193 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001194 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001195 for copyfile in self.copyfiles:
1196 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001197 for linkfile in self.linkfiles:
1198 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001199
Julien Camperguedd654222014-01-09 16:21:37 +01001200 def GetCommitRevisionId(self):
1201 """Get revisionId of a commit.
1202
1203 Use this method instead of GetRevisionId to get the id of the commit rather
1204 than the id of the current git object (for example, a tag)
1205
1206 """
1207 if not self.revisionExpr.startswith(R_TAGS):
1208 return self.GetRevisionId(self._allrefs)
1209
1210 try:
1211 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1212 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001213 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1214 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001215
David Pursehouse8a68ff92012-09-24 12:15:13 +09001216 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001217 if self.revisionId:
1218 return self.revisionId
1219
1220 rem = self.GetRemote(self.remote.name)
1221 rev = rem.ToLocal(self.revisionExpr)
1222
David Pursehouse8a68ff92012-09-24 12:15:13 +09001223 if all_refs is not None and rev in all_refs:
1224 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001225
1226 try:
1227 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1228 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001229 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1230 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001231
Raman Tenneti6a872c92021-01-14 19:17:50 -08001232 def SetRevisionId(self, revisionId):
Xin Li0e776a52021-06-29 21:42:34 +00001233 if self.revisionExpr:
Xin Liaabf79d2021-04-29 01:50:38 -07001234 self.upstream = self.revisionExpr
1235
Raman Tenneti6a872c92021-01-14 19:17:50 -08001236 self.revisionId = revisionId
1237
Martin Kellye4e94d22017-03-21 16:05:12 -07001238 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001239 """Perform only the local IO portion of the sync process.
1240 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001241 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001242 if not os.path.exists(self.gitdir):
1243 syncbuf.fail(self,
1244 'Cannot checkout %s due to missing network sync; Run '
1245 '`repo sync -n %s` first.' %
1246 (self.name, self.name))
1247 return
1248
Martin Kellye4e94d22017-03-21 16:05:12 -07001249 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001250 all_refs = self.bare_ref.all
1251 self.CleanPublishedCache(all_refs)
1252 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001253
Mike Frysinger0458faa2021-03-10 23:35:44 -05001254 # Special case the root of the repo client checkout. Make sure it doesn't
1255 # contain files being checked out to dirs we don't allow.
1256 if self.relpath == '.':
1257 PROTECTED_PATHS = {'.repo'}
1258 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1259 bad_paths = paths & PROTECTED_PATHS
1260 if bad_paths:
1261 syncbuf.fail(self,
1262 'Refusing to checkout project that writes to protected '
1263 'paths: %s' % (', '.join(bad_paths),))
1264 return
1265
David Pursehouse1d947b32012-10-25 12:23:11 +09001266 def _doff():
1267 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001268 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001269
Martin Kellye4e94d22017-03-21 16:05:12 -07001270 def _dosubmodules():
1271 self._SyncSubmodules(quiet=True)
1272
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001273 head = self.work_git.GetHead()
1274 if head.startswith(R_HEADS):
1275 branch = head[len(R_HEADS):]
1276 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001277 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001278 except KeyError:
1279 head = None
1280 else:
1281 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001282
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001283 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001284 # Currently on a detached HEAD. The user is assumed to
1285 # not have any local modifications worth worrying about.
1286 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001287 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001288 syncbuf.fail(self, _PriorSyncFailedError())
1289 return
1290
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001291 if head == revid:
1292 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001293 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001294 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001295 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001296 # The copy/linkfile config may have changed.
1297 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001298 return
1299 else:
1300 lost = self._revlist(not_rev(revid), HEAD)
1301 if lost:
1302 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001303
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001304 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001305 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001306 if submodules:
1307 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001308 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001309 syncbuf.fail(self, e)
1310 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001311 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001312 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001313
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001314 if head == revid:
1315 # No changes; don't do anything further.
1316 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001317 # The copy/linkfile config may have changed.
1318 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001319 return
1320
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001321 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001322
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001323 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001324 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001325 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001326 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001327 syncbuf.info(self,
1328 "leaving %s; does not track upstream",
1329 branch.name)
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. Pearce3c8dea12009-05-29 18:38:17 -07001340 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001341
1342 # See if we can perform a fast forward merge. This can happen if our
1343 # branch isn't in the exact same state as we last published.
1344 try:
1345 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1346 # Skip the published logic.
1347 pub = False
1348 except GitError:
1349 pub = self.WasPublished(branch.name, all_refs)
1350
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001351 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001352 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001353 if not_merged:
1354 if upstream_gain:
1355 # The user has published this branch and some of those
1356 # commits are not yet merged upstream. We do not want
1357 # to rewrite the published commits so we punt.
1358 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001359 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001360 "branch %s is published (but not merged) and is now "
1361 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001362 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001363 elif pub == head:
1364 # All published commits are merged, and thus we are a
1365 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001366 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001367 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001368 if submodules:
1369 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001370 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001371
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001372 # Examine the local commits not in the remote. Find the
1373 # last one attributed to this user, if any.
1374 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001375 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001376 last_mine = None
1377 cnt_mine = 0
1378 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001379 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001380 if committer_email == self.UserEmail:
1381 last_mine = commit_id
1382 cnt_mine += 1
1383
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001384 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001385 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001386
1387 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001388 syncbuf.fail(self, _DirtyError())
1389 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001390
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001391 # If the upstream switched on us, warn the user.
1392 #
1393 if branch.merge != self.revisionExpr:
1394 if branch.merge and self.revisionExpr:
1395 syncbuf.info(self,
1396 'manifest switched %s...%s',
1397 branch.merge,
1398 self.revisionExpr)
1399 elif branch.merge:
1400 syncbuf.info(self,
1401 'manifest no longer tracks %s',
1402 branch.merge)
1403
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001404 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001406 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001407 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001408 syncbuf.info(self,
1409 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001410 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001411
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001412 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001413 if not ID_RE.match(self.revisionExpr):
1414 # in case of manifest sync the revisionExpr might be a SHA1
1415 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001416 if not branch.merge.startswith('refs/'):
1417 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001418 branch.Save()
1419
Mike Pontillod3153822012-02-28 11:53:24 -08001420 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001421 def _docopyandlink():
1422 self._CopyAndLinkFiles()
1423
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001424 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001425 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001426 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001427 if submodules:
1428 syncbuf.later2(self, _dosubmodules)
1429 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001430 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001431 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001432 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001433 if submodules:
1434 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001435 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001436 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001437 syncbuf.fail(self, e)
1438 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001439 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001440 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001441 if submodules:
1442 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443
Mike Frysingere6a202f2019-08-02 15:57:57 -04001444 def AddCopyFile(self, src, dest, topdir):
1445 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001446
Mike Frysingere6a202f2019-08-02 15:57:57 -04001447 No filesystem changes occur here. Actual copying happens later on.
1448
1449 Paths should have basic validation run on them before being queued.
1450 Further checking will be handled when the actual copy happens.
1451 """
1452 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1453
1454 def AddLinkFile(self, src, dest, topdir):
1455 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1456
1457 No filesystem changes occur here. Actual linking happens later on.
1458
1459 Paths should have basic validation run on them before being queued.
1460 Further checking will be handled when the actual link happens.
1461 """
1462 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001463
James W. Mills24c13082012-04-12 15:04:13 -05001464 def AddAnnotation(self, name, value, keep):
Jack Neus6ea0cae2021-07-20 20:52:33 +00001465 self.annotations.append(Annotation(name, value, keep))
James W. Mills24c13082012-04-12 15:04:13 -05001466
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001467 def DownloadPatchSet(self, change_id, patch_id):
1468 """Download a single patch set of a single change to FETCH_HEAD.
1469 """
1470 remote = self.GetRemote(self.remote.name)
1471
1472 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001473 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001474 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001475 if GitCommand(self, cmd, bare=True).Wait() != 0:
1476 return None
1477 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001478 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001479 change_id,
1480 patch_id,
1481 self.bare_git.rev_parse('FETCH_HEAD'))
1482
Mike Frysingerc0d18662020-02-19 19:19:18 -05001483 def DeleteWorktree(self, quiet=False, force=False):
1484 """Delete the source checkout and any other housekeeping tasks.
1485
1486 This currently leaves behind the internal .repo/ cache state. This helps
1487 when switching branches or manifest changes get reverted as we don't have
1488 to redownload all the git objects. But we should do some GC at some point.
1489
1490 Args:
1491 quiet: Whether to hide normal messages.
1492 force: Always delete tree even if dirty.
1493
1494 Returns:
1495 True if the worktree was completely cleaned out.
1496 """
1497 if self.IsDirty():
1498 if force:
1499 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1500 (self.relpath,), file=sys.stderr)
1501 else:
1502 print('error: %s: Cannot remove project: uncommitted changes are '
1503 'present.\n' % (self.relpath,), file=sys.stderr)
1504 return False
1505
1506 if not quiet:
1507 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1508
1509 # Unlock and delink from the main worktree. We don't use git's worktree
1510 # remove because it will recursively delete projects -- we handle that
1511 # ourselves below. https://crbug.com/git/48
1512 if self.use_git_worktrees:
1513 needle = platform_utils.realpath(self.gitdir)
1514 # Find the git worktree commondir under .repo/worktrees/.
1515 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1516 assert output.startswith('worktree '), output
1517 commondir = output[9:]
1518 # Walk each of the git worktrees to see where they point.
1519 configs = os.path.join(commondir, 'worktrees')
1520 for name in os.listdir(configs):
1521 gitdir = os.path.join(configs, name, 'gitdir')
1522 with open(gitdir) as fp:
1523 relpath = fp.read().strip()
1524 # Resolve the checkout path and see if it matches this project.
1525 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1526 if fullpath == needle:
1527 platform_utils.rmtree(os.path.join(configs, name))
1528
1529 # Delete the .git directory first, so we're less likely to have a partially
1530 # working git repository around. There shouldn't be any git projects here,
1531 # so rmtree works.
1532
1533 # Try to remove plain files first in case of git worktrees. If this fails
1534 # for any reason, we'll fall back to rmtree, and that'll display errors if
1535 # it can't remove things either.
1536 try:
1537 platform_utils.remove(self.gitdir)
1538 except OSError:
1539 pass
1540 try:
1541 platform_utils.rmtree(self.gitdir)
1542 except OSError as e:
1543 if e.errno != errno.ENOENT:
1544 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1545 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1546 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1547 return False
1548
1549 # Delete everything under the worktree, except for directories that contain
1550 # another git project.
1551 dirs_to_remove = []
1552 failed = False
1553 for root, dirs, files in platform_utils.walk(self.worktree):
1554 for f in files:
1555 path = os.path.join(root, f)
1556 try:
1557 platform_utils.remove(path)
1558 except OSError as e:
1559 if e.errno != errno.ENOENT:
1560 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1561 failed = True
1562 dirs[:] = [d for d in dirs
1563 if not os.path.lexists(os.path.join(root, d, '.git'))]
1564 dirs_to_remove += [os.path.join(root, d) for d in dirs
1565 if os.path.join(root, d) not in dirs_to_remove]
1566 for d in reversed(dirs_to_remove):
1567 if platform_utils.islink(d):
1568 try:
1569 platform_utils.remove(d)
1570 except OSError as e:
1571 if e.errno != errno.ENOENT:
1572 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1573 failed = True
1574 elif not platform_utils.listdir(d):
1575 try:
1576 platform_utils.rmdir(d)
1577 except OSError as e:
1578 if e.errno != errno.ENOENT:
1579 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1580 failed = True
1581 if failed:
1582 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1583 file=sys.stderr)
1584 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1585 return False
1586
1587 # Try deleting parent dirs if they are empty.
1588 path = self.worktree
1589 while path != self.manifest.topdir:
1590 try:
1591 platform_utils.rmdir(path)
1592 except OSError as e:
1593 if e.errno != errno.ENOENT:
1594 break
1595 path = os.path.dirname(path)
1596
1597 return True
1598
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001599# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001600 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001601 """Create a new branch off the manifest's revision.
1602 """
Simran Basib9a1b732015-08-20 12:19:28 -07001603 if not branch_merge:
1604 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001605 head = self.work_git.GetHead()
1606 if head == (R_HEADS + name):
1607 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001608
David Pursehouse8a68ff92012-09-24 12:15:13 +09001609 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001610 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001611 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001612 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001613 capture_stdout=True,
1614 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001615
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001616 branch = self.GetBranch(name)
1617 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001618 branch.merge = branch_merge
1619 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1620 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001621
1622 if revision is None:
1623 revid = self.GetRevisionId(all_refs)
1624 else:
1625 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001626
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001627 if head.startswith(R_HEADS):
1628 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001629 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001630 except KeyError:
1631 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001632 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001633 ref = R_HEADS + name
1634 self.work_git.update_ref(ref, revid)
1635 self.work_git.symbolic_ref(HEAD, ref)
1636 branch.Save()
1637 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001638
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001639 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001640 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001641 capture_stdout=True,
1642 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001643 branch.Save()
1644 return True
1645 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001646
Wink Saville02d79452009-04-10 13:01:24 -07001647 def CheckoutBranch(self, name):
1648 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001649
1650 Args:
1651 name: The name of the branch to checkout.
1652
1653 Returns:
1654 True if the checkout succeeded; False if it didn't; None if the branch
1655 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001656 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001657 rev = R_HEADS + name
1658 head = self.work_git.GetHead()
1659 if head == rev:
1660 # Already on the branch
1661 #
1662 return True
Wink Saville02d79452009-04-10 13:01:24 -07001663
David Pursehouse8a68ff92012-09-24 12:15:13 +09001664 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001665 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001667 except KeyError:
1668 # Branch does not exist in this project
1669 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001670 return None
Wink Saville02d79452009-04-10 13:01:24 -07001671
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001672 if head.startswith(R_HEADS):
1673 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001674 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001675 except KeyError:
1676 head = None
1677
1678 if head == revid:
1679 # Same revision; just update HEAD to point to the new
1680 # target branch, but otherwise take no other action.
1681 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001682 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1683 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001684 return True
1685
1686 return GitCommand(self,
1687 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001688 capture_stdout=True,
1689 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001690
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001691 def AbandonBranch(self, name):
1692 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001693
1694 Args:
1695 name: The name of the branch to abandon.
1696
1697 Returns:
1698 True if the abandon succeeded; False if it didn't; None if the branch
1699 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001700 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001701 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001702 all_refs = self.bare_ref.all
1703 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001704 # Doesn't exist
1705 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001706
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001707 head = self.work_git.GetHead()
1708 if head == rev:
1709 # We can't destroy the branch while we are sitting
1710 # on it. Switch to a detached HEAD.
1711 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001712 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001713
David Pursehouse8a68ff92012-09-24 12:15:13 +09001714 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001715 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001716 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001717 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001718 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001719
1720 return GitCommand(self,
1721 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001722 capture_stdout=True,
1723 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001724
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001725 def PruneHeads(self):
1726 """Prune any topic branches already merged into upstream.
1727 """
1728 cb = self.CurrentBranch
1729 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001730 left = self._allrefs
1731 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732 if name.startswith(R_HEADS):
1733 name = name[len(R_HEADS):]
1734 if cb is None or name != cb:
1735 kill.append(name)
1736
Mike Frysingera3794e92021-03-11 23:24:01 -05001737 # Minor optimization: If there's nothing to prune, then don't try to read
1738 # any project state.
1739 if not kill and not cb:
1740 return []
1741
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001742 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001743 if cb is not None \
1744 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001745 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001746 self.work_git.DetachHead(HEAD)
1747 kill.append(cb)
1748
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001749 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001750 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001751
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001752 try:
1753 self.bare_git.DetachHead(rev)
1754
1755 b = ['branch', '-d']
1756 b.extend(kill)
1757 b = GitCommand(self, b, bare=True,
1758 capture_stdout=True,
1759 capture_stderr=True)
1760 b.Wait()
1761 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001762 if ID_RE.match(old):
1763 self.bare_git.DetachHead(old)
1764 else:
1765 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001766 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001767
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001768 for branch in kill:
1769 if (R_HEADS + branch) not in left:
1770 self.CleanPublishedCache()
1771 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001772
1773 if cb and cb not in kill:
1774 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001775 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001776
1777 kept = []
1778 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001779 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001780 branch = self.GetBranch(branch)
1781 base = branch.LocalMerge
1782 if not base:
1783 base = rev
1784 kept.append(ReviewableBranch(self, branch, base))
1785 return kept
1786
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001787# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001788 def GetRegisteredSubprojects(self):
1789 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001790
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001791 def rec(subprojects):
1792 if not subprojects:
1793 return
1794 result.extend(subprojects)
1795 for p in subprojects:
1796 rec(p.subprojects)
1797 rec(self.subprojects)
1798 return result
1799
1800 def _GetSubmodules(self):
1801 # Unfortunately we cannot call `git submodule status --recursive` here
1802 # because the working tree might not exist yet, and it cannot be used
1803 # without a working tree in its current implementation.
1804
1805 def get_submodules(gitdir, rev):
1806 # Parse .gitmodules for submodule sub_paths and sub_urls
1807 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1808 if not sub_paths:
1809 return []
1810 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1811 # revision of submodule repository
1812 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1813 submodules = []
1814 for sub_path, sub_url in zip(sub_paths, sub_urls):
1815 try:
1816 sub_rev = sub_revs[sub_path]
1817 except KeyError:
1818 # Ignore non-exist submodules
1819 continue
1820 submodules.append((sub_rev, sub_path, sub_url))
1821 return submodules
1822
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001823 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1824 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001825
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001826 def parse_gitmodules(gitdir, rev):
1827 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1828 try:
Anthony King7bdac712014-07-16 12:56:40 +01001829 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1830 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001831 except GitError:
1832 return [], []
1833 if p.Wait() != 0:
1834 return [], []
1835
1836 gitmodules_lines = []
1837 fd, temp_gitmodules_path = tempfile.mkstemp()
1838 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001839 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001840 os.close(fd)
1841 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001842 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1843 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001844 if p.Wait() != 0:
1845 return [], []
1846 gitmodules_lines = p.stdout.split('\n')
1847 except GitError:
1848 return [], []
1849 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001850 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001851
1852 names = set()
1853 paths = {}
1854 urls = {}
1855 for line in gitmodules_lines:
1856 if not line:
1857 continue
1858 m = re_path.match(line)
1859 if m:
1860 names.add(m.group(1))
1861 paths[m.group(1)] = m.group(2)
1862 continue
1863 m = re_url.match(line)
1864 if m:
1865 names.add(m.group(1))
1866 urls[m.group(1)] = m.group(2)
1867 continue
1868 names = sorted(names)
1869 return ([paths.get(name, '') for name in names],
1870 [urls.get(name, '') for name in names])
1871
1872 def git_ls_tree(gitdir, rev, paths):
1873 cmd = ['ls-tree', rev, '--']
1874 cmd.extend(paths)
1875 try:
Anthony King7bdac712014-07-16 12:56:40 +01001876 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1877 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001878 except GitError:
1879 return []
1880 if p.Wait() != 0:
1881 return []
1882 objects = {}
1883 for line in p.stdout.split('\n'):
1884 if not line.strip():
1885 continue
1886 object_rev, object_path = line.split()[2:4]
1887 objects[object_path] = object_rev
1888 return objects
1889
1890 try:
1891 rev = self.GetRevisionId()
1892 except GitError:
1893 return []
1894 return get_submodules(self.gitdir, rev)
1895
1896 def GetDerivedSubprojects(self):
1897 result = []
1898 if not self.Exists:
1899 # If git repo does not exist yet, querying its submodules will
1900 # mess up its states; so return here.
1901 return result
1902 for rev, path, url in self._GetSubmodules():
1903 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001904 relpath, worktree, gitdir, objdir = \
1905 self.manifest.GetSubprojectPaths(self, name, path)
1906 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001907 if project:
1908 result.extend(project.GetDerivedSubprojects())
1909 continue
David James8d201162013-10-11 17:03:19 -07001910
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001911 if url.startswith('..'):
1912 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001913 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001914 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001915 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001916 review=self.remote.review,
1917 revision=self.remote.revision)
1918 subproject = Project(manifest=self.manifest,
1919 name=name,
1920 remote=remote,
1921 gitdir=gitdir,
1922 objdir=objdir,
1923 worktree=worktree,
1924 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001925 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001926 revisionId=rev,
1927 rebase=self.rebase,
1928 groups=self.groups,
1929 sync_c=self.sync_c,
1930 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001931 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001932 parent=self,
1933 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001934 result.append(subproject)
1935 result.extend(subproject.GetDerivedSubprojects())
1936 return result
1937
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001938# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001939 def EnableRepositoryExtension(self, key, value='true', version=1):
1940 """Enable git repository extension |key| with |value|.
1941
1942 Args:
1943 key: The extension to enabled. Omit the "extensions." prefix.
1944 value: The value to use for the extension.
1945 version: The minimum git repository version needed.
1946 """
1947 # Make sure the git repo version is new enough already.
1948 found_version = self.config.GetInt('core.repositoryFormatVersion')
1949 if found_version is None:
1950 found_version = 0
1951 if found_version < version:
1952 self.config.SetString('core.repositoryFormatVersion', str(version))
1953
1954 # Enable the extension!
1955 self.config.SetString('extensions.%s' % (key,), value)
1956
Mike Frysinger50a81de2020-09-06 15:51:21 -04001957 def ResolveRemoteHead(self, name=None):
1958 """Find out what the default branch (HEAD) points to.
1959
1960 Normally this points to refs/heads/master, but projects are moving to main.
1961 Support whatever the server uses rather than hardcoding "master" ourselves.
1962 """
1963 if name is None:
1964 name = self.remote.name
1965
1966 # The output will look like (NB: tabs are separators):
1967 # ref: refs/heads/master HEAD
1968 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1969 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1970
1971 for line in output.splitlines():
1972 lhs, rhs = line.split('\t', 1)
1973 if rhs == 'HEAD' and lhs.startswith('ref:'):
1974 return lhs[4:].strip()
1975
1976 return None
1977
Zac Livingstone4332262017-06-16 08:56:09 -06001978 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001979 try:
1980 # if revision (sha or tag) is not present then following function
1981 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001982 self.bare_git.rev_list('-1', '--missing=allow-any',
1983 '%s^0' % self.revisionExpr, '--')
Xin Li0e776a52021-06-29 21:42:34 +00001984 if self.upstream:
1985 rev = self.GetRemote(self.remote.name).ToLocal(self.upstream)
1986 self.bare_git.rev_list('-1', '--missing=allow-any',
1987 '%s^0' % rev, '--')
Xin Li8e983bb2021-07-20 20:15:30 +00001988 self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev)
Chris AtLee2fb64662014-01-16 21:32:33 -05001989 return True
1990 except GitError:
1991 # There is no such persistent revision. We have to fetch it.
1992 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001993
Julien Campergue335f5ef2013-10-16 11:02:35 +02001994 def _FetchArchive(self, tarpath, cwd=None):
1995 cmd = ['archive', '-v', '-o', tarpath]
1996 cmd.append('--remote=%s' % self.remote.url)
1997 cmd.append('--prefix=%s/' % self.relpath)
1998 cmd.append(self.revisionExpr)
1999
2000 command = GitCommand(self, cmd, cwd=cwd,
2001 capture_stdout=True,
2002 capture_stderr=True)
2003
2004 if command.Wait() != 0:
2005 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2006
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002007 def _RemoteFetch(self, name=None,
2008 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002009 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002010 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002011 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002012 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002013 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002014 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002015 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002016 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002017 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002018 ssh_proxy=None,
Xin Li745be2e2019-06-03 11:24:30 -07002019 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002020 clone_filter=None,
2021 retry_fetches=2,
2022 retry_sleep_initial_sec=4.0,
2023 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002024 is_sha1 = False
2025 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002026 # The depth should not be used when fetching to a mirror because
2027 # it will result in a shallow repository that cannot be cloned or
2028 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002029 # The repo project should also never be synced with partial depth.
2030 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2031 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002032
Shawn Pearce69e04d82014-01-29 12:48:54 -08002033 if depth:
2034 current_branch_only = True
2035
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002036 if ID_RE.match(self.revisionExpr) is not None:
2037 is_sha1 = True
2038
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002039 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002040 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002041 # this is a tag and its sha1 value should never change
2042 tag_name = self.revisionExpr[len(R_TAGS):]
2043
2044 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002045 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002046 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002047 print('Skipped fetching project %s (already have persistent ref)'
2048 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002049 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002050 if is_sha1 and not depth:
2051 # When syncing a specific commit and --depth is not set:
2052 # * if upstream is explicitly specified and is not a sha1, fetch only
2053 # upstream as users expect only upstream to be fetch.
2054 # Note: The commit might not be in upstream in which case the sync
2055 # will fail.
2056 # * otherwise, fetch all branches to make sure we end up with the
2057 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002058 if self.upstream:
2059 current_branch_only = not ID_RE.match(self.upstream)
2060 else:
2061 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002062
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002063 if not name:
2064 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002065
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002066 remote = self.GetRemote(name)
Mike Frysinger339f2df2021-05-06 00:44:42 -04002067 if not remote.PreConnectFetch(ssh_proxy):
2068 ssh_proxy = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002069
Shawn O. Pearce88443382010-10-08 10:02:09 +02002070 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002071 if alt_dir and 'objects' == os.path.basename(alt_dir):
2072 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002073 packed_refs = os.path.join(self.gitdir, 'packed-refs')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002074
David Pursehouse8a68ff92012-09-24 12:15:13 +09002075 all_refs = self.bare_ref.all
2076 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002077 tmp = set()
2078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302079 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002080 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002081 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002082 all_refs[r] = ref_id
2083 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002084 continue
2085
David Pursehouse8a68ff92012-09-24 12:15:13 +09002086 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002087 continue
2088
David Pursehouse8a68ff92012-09-24 12:15:13 +09002089 r = 'refs/_alt/%s' % ref_id
2090 all_refs[r] = ref_id
2091 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002092 tmp.add(r)
2093
heping3d7bbc92017-04-12 19:51:47 +08002094 tmp_packed_lines = []
2095 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302097 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002098 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002099 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002101 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002102
heping3d7bbc92017-04-12 19:51:47 +08002103 tmp_packed = ''.join(tmp_packed_lines)
2104 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002105 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002107 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002108
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002109 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002110
Xin Li745be2e2019-06-03 11:24:30 -07002111 if clone_filter:
2112 git_require((2, 19, 0), fail=True, msg='partial clones')
2113 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002114 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002115
Conley Owensf97e8382015-01-21 11:12:46 -08002116 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002117 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002118 else:
2119 # If this repo has shallow objects, then we don't know which refs have
2120 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2121 # do this with projects that don't have shallow objects, since it is less
2122 # efficient.
2123 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2124 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002125
Mike Frysinger4847e052020-02-22 00:07:35 -05002126 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002127 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002128 if not quiet and sys.stdout.isatty():
2129 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002130 if not self.worktree:
2131 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002132 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002133
Mike Frysingere57f1142019-03-18 21:27:54 -04002134 if force_sync:
2135 cmd.append('--force')
2136
David Pursehouse74cfd272015-10-14 10:50:15 +09002137 if prune:
2138 cmd.append('--prune')
2139
Martin Kellye4e94d22017-03-21 16:05:12 -07002140 if submodules:
2141 cmd.append('--recurse-submodules=on-demand')
2142
Kuang-che Wu6856f982019-11-25 12:37:55 +08002143 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002144 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002145 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002146 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002147 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002148 spec.append('tag')
2149 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002150
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302151 if self.manifest.IsMirror and not current_branch_only:
2152 branch = None
2153 else:
2154 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002155 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002156 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002157 # Shallow checkout of a specific commit, fetch from that commit and not
2158 # the heads only as the commit might be deeper in the history.
2159 spec.append(branch)
Xin Liaabf79d2021-04-29 01:50:38 -07002160 if self.upstream:
2161 spec.append(self.upstream)
Kuang-che Wu6856f982019-11-25 12:37:55 +08002162 else:
2163 if is_sha1:
2164 branch = self.upstream
2165 if branch is not None and branch.strip():
2166 if not branch.startswith('refs/'):
2167 branch = R_HEADS + branch
2168 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2169
2170 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2171 # whole repo.
2172 if self.manifest.IsMirror and not spec:
2173 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2174
2175 # If using depth then we should not get all the tags since they may
2176 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002177 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002178 cmd.append('--no-tags')
2179 else:
2180 cmd.append('--tags')
2181 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2182
Conley Owens80b87fe2014-05-09 17:13:44 -07002183 cmd.extend(spec)
2184
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002185 # At least one retry minimum due to git remote prune.
2186 retry_fetches = max(retry_fetches, 2)
2187 retry_cur_sleep = retry_sleep_initial_sec
2188 ok = prune_tried = False
2189 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002190 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002191 merge_output=True, capture_stdout=quiet or bool(output_redir))
2192 if gitcmd.stdout and not quiet and output_redir:
2193 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002194 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002195 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002196 ok = True
2197 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002198
2199 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002200 elif (gitcmd.stdout and
2201 'error:' in gitcmd.stdout and
2202 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002203 # Fallthru to sleep+retry logic at the bottom.
2204 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002205
Mike Frysinger6823bc22021-04-15 02:06:28 -04002206 # Try to prune remote branches once in case there are conflicts.
2207 # For example, if the remote had refs/heads/upstream, but deleted that and
2208 # now has refs/heads/upstream/foo.
2209 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002210 'error:' in gitcmd.stdout and
2211 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002212 not prune_tried):
2213 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002214 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002215 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002216 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002217 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002218 break
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002219 print('retrying fetch after pruning remote branches', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002220 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002221 continue
Brian Harring14a66742012-09-28 20:21:57 -07002222 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002223 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2224 # in sha1 mode, we just tried sync'ing from the upstream field; it
2225 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002226 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002227 elif ret < 0:
2228 # Git died with a signal, exit immediately
2229 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002230
2231 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002232 if not verbose and gitcmd.stdout:
2233 print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002234 if try_n < retry_fetches - 1:
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002235 print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep),
2236 file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002237 time.sleep(retry_cur_sleep)
2238 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2239 MAXIMUM_RETRY_SLEEP_SEC)
2240 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2241 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002242
2243 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002244 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002245 if old_packed != '':
2246 _lwrite(packed_refs, old_packed)
2247 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002248 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002249 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002250
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002251 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002252 # We just synced the upstream given branch; verify we
2253 # got what we wanted, else trigger a second run of all
2254 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002255 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002256 # Sync the current branch only with depth set to None.
2257 # We always pass depth=None down to avoid infinite recursion.
2258 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002259 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002260 current_branch_only=current_branch_only and depth,
2261 initial=False, alt_dir=alt_dir,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002262 depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002263
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002264 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002265
Mike Frysingere50b6a72020-02-19 01:45:48 -05002266 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002267 if initial and \
2268 (self.manifest.manifestProject.config.GetString('repo.depth') or
2269 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002270 return False
2271
2272 remote = self.GetRemote(self.remote.name)
2273 bundle_url = remote.url + '/clone.bundle'
2274 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002275 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2276 'persistent-http',
2277 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002278 return False
2279
2280 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2281 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2282
2283 exist_dst = os.path.exists(bundle_dst)
2284 exist_tmp = os.path.exists(bundle_tmp)
2285
2286 if not initial and not exist_dst and not exist_tmp:
2287 return False
2288
2289 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002290 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2291 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002292 if not exist_dst:
2293 return False
2294
2295 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002296 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002297 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002298 if not quiet and sys.stdout.isatty():
2299 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002300 if not self.worktree:
2301 cmd.append('--update-head-ok')
2302 cmd.append(bundle_dst)
2303 for f in remote.fetch:
2304 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002305 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002306
2307 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Mike Frysinger9d96f582021-09-28 11:27:24 -04002308 platform_utils.remove(bundle_dst, missing_ok=True)
2309 platform_utils.remove(bundle_tmp, missing_ok=True)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002310 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002311
Mike Frysingere50b6a72020-02-19 01:45:48 -05002312 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002313 platform_utils.remove(dstPath, missing_ok=True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002314
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002315 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002316 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002317 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002318 if os.path.exists(tmpPath):
2319 size = os.stat(tmpPath).st_size
2320 if size >= 1024:
2321 cmd += ['--continue-at', '%d' % (size,)]
2322 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002323 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002324 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002325 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002326 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002327 if proxy:
2328 cmd += ['--proxy', proxy]
2329 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2330 cmd += ['--proxy', os.environ['http_proxy']]
2331 if srcUrl.startswith('persistent-https'):
2332 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2333 elif srcUrl.startswith('persistent-http'):
2334 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002335 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002336
Dave Borowitz137d0132015-01-02 11:12:54 -08002337 if IsTrace():
2338 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002339 if verbose:
2340 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2341 stdout = None if verbose else subprocess.PIPE
2342 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002343 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002344 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002345 except OSError:
2346 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002347
Mike Frysingere50b6a72020-02-19 01:45:48 -05002348 (output, _) = proc.communicate()
2349 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002350
Dave Borowitz137d0132015-01-02 11:12:54 -08002351 if curlret == 22:
2352 # From curl man page:
2353 # 22: HTTP page not retrieved. The requested url was not found or
2354 # returned another error with the HTTP error code being 400 or above.
2355 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002356 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002357 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2358 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002359 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002360 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002361 elif curlret and not verbose and output:
2362 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002363
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002364 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002365 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002366 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002367 return True
2368 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002369 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002370 return False
2371 else:
2372 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002373
Kris Giesingc8d882a2014-12-23 13:02:32 -08002374 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002375 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002376 with open(path, 'rb') as f:
2377 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002378 return True
2379 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002380 if not quiet:
2381 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002382 return False
2383 except OSError:
2384 return False
2385
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002386 def _Checkout(self, rev, quiet=False):
2387 cmd = ['checkout']
2388 if quiet:
2389 cmd.append('-q')
2390 cmd.append(rev)
2391 cmd.append('--')
2392 if GitCommand(self, cmd).Wait() != 0:
2393 if self._allrefs:
2394 raise GitError('%s checkout %s ' % (self.name, rev))
2395
Mike Frysinger915fda12020-03-22 12:15:20 -04002396 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002397 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002398 if ffonly:
2399 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002400 if record_origin:
2401 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002402 cmd.append(rev)
2403 cmd.append('--')
2404 if GitCommand(self, cmd).Wait() != 0:
2405 if self._allrefs:
2406 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2407
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302408 def _LsRemote(self, refs):
2409 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302410 p = GitCommand(self, cmd, capture_stdout=True)
2411 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002412 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302413 return None
2414
Anthony King7bdac712014-07-16 12:56:40 +01002415 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002416 cmd = ['revert']
2417 cmd.append('--no-edit')
2418 cmd.append(rev)
2419 cmd.append('--')
2420 if GitCommand(self, cmd).Wait() != 0:
2421 if self._allrefs:
2422 raise GitError('%s revert %s ' % (self.name, rev))
2423
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002424 def _ResetHard(self, rev, quiet=True):
2425 cmd = ['reset', '--hard']
2426 if quiet:
2427 cmd.append('-q')
2428 cmd.append(rev)
2429 if GitCommand(self, cmd).Wait() != 0:
2430 raise GitError('%s reset --hard %s ' % (self.name, rev))
2431
Martin Kellye4e94d22017-03-21 16:05:12 -07002432 def _SyncSubmodules(self, quiet=True):
2433 cmd = ['submodule', 'update', '--init', '--recursive']
2434 if quiet:
2435 cmd.append('-q')
2436 if GitCommand(self, cmd).Wait() != 0:
2437 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2438
Anthony King7bdac712014-07-16 12:56:40 +01002439 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002440 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002441 if onto is not None:
2442 cmd.extend(['--onto', onto])
2443 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002444 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002445 raise GitError('%s rebase %s ' % (self.name, upstream))
2446
Pierre Tardy3d125942012-05-04 12:18:12 +02002447 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002448 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002449 if ffonly:
2450 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002451 if GitCommand(self, cmd).Wait() != 0:
2452 raise GitError('%s merge %s ' % (self.name, head))
2453
David Pursehousee8ace262020-02-13 12:41:15 +09002454 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002455 init_git_dir = not os.path.exists(self.gitdir)
2456 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002457 try:
2458 # Initialize the bare repository, which contains all of the objects.
2459 if init_obj_dir:
2460 os.makedirs(self.objdir)
2461 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002462
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002463 if self.use_git_worktrees:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002464 # Enable per-worktree config file support if possible. This is more a
2465 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002466 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002467 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002468
Kevin Degib1a07b82015-07-27 13:33:43 -06002469 # If we have a separate directory to hold refs, initialize it as well.
2470 if self.objdir != self.gitdir:
2471 if init_git_dir:
2472 os.makedirs(self.gitdir)
2473
2474 if init_obj_dir or init_git_dir:
2475 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2476 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002477 try:
2478 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2479 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002480 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002481 print("Retrying clone after deleting %s" %
2482 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002483 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002484 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2485 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002486 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002487 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002488 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2489 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002490 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002491 raise e
2492 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002493
Kevin Degi384b3c52014-10-16 16:02:58 -06002494 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002495 mp = self.manifest.manifestProject
2496 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002497
Kevin Degib1a07b82015-07-27 13:33:43 -06002498 if ref_dir or mirror_git:
2499 if not mirror_git:
2500 mirror_git = os.path.join(ref_dir, self.name + '.git')
2501 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2502 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002503 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2504 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002505
Kevin Degib1a07b82015-07-27 13:33:43 -06002506 if os.path.exists(mirror_git):
2507 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002508 elif os.path.exists(repo_git):
2509 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002510 elif os.path.exists(worktrees_git):
2511 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002512 else:
2513 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002514
Kevin Degib1a07b82015-07-27 13:33:43 -06002515 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002516 if not os.path.isabs(ref_dir):
2517 # The alternate directory is relative to the object database.
2518 ref_dir = os.path.relpath(ref_dir,
2519 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002520 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2521 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002522
David Pursehousee8ace262020-02-13 12:41:15 +09002523 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002524
2525 m = self.manifest.manifestProject.config
2526 for key in ['user.name', 'user.email']:
2527 if m.Has(key, include_defaults=False):
2528 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002529 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002530 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002531 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002532 except Exception:
2533 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002534 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002535 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002536 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002537 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002538
David Pursehousee8ace262020-02-13 12:41:15 +09002539 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002540 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002541 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002542
David Pursehousee8ace262020-02-13 12:41:15 +09002543 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002544 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002545 if not os.path.exists(hooks):
2546 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002547 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002548 name = os.path.basename(stock_hook)
2549
Victor Boivie65e0f352011-04-18 11:23:29 +02002550 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002551 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002552 # Don't install a Gerrit Code Review hook if this
2553 # project does not appear to use it for reviews.
2554 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002555 # Since the manifest project is one of those, but also
2556 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002557 continue
2558
2559 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002560 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002561 continue
2562 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002563 # If the files are the same, we'll leave it alone. We create symlinks
2564 # below by default but fallback to hardlinks if the OS blocks them.
2565 # So if we're here, it's probably because we made a hardlink below.
2566 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002567 if not quiet:
2568 _warn("%s: Not replacing locally modified %s hook",
2569 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002570 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002571 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002572 platform_utils.symlink(
2573 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002574 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002575 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002576 try:
2577 os.link(stock_hook, dst)
2578 except OSError:
2579 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002580 else:
2581 raise
2582
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002583 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002584 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002585 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002586 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002587 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002588 remote.review = self.remote.review
2589 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002590
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002591 if self.worktree:
2592 remote.ResetFetch(mirror=False)
2593 else:
2594 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595 remote.Save()
2596
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002597 def _InitMRef(self):
2598 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002599 if self.use_git_worktrees:
Mike Frysinger29626b42021-05-01 09:37:13 -04002600 # Set up the m/ space to point to the worktree-specific ref space.
2601 # We'll update the worktree-specific ref space on each checkout.
2602 ref = R_M + self.manifest.branch
2603 if not self.bare_ref.symref(ref):
2604 self.bare_git.symbolic_ref(
2605 '-m', 'redirecting to worktree scope',
2606 ref, R_WORKTREE_M + self.manifest.branch)
2607
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002608 # We can't update this ref with git worktrees until it exists.
2609 # We'll wait until the initial checkout to set it.
2610 if not os.path.exists(self.worktree):
2611 return
2612
2613 base = R_WORKTREE_M
2614 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002615
2616 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002617 else:
2618 base = R_M
2619 active_git = self.bare_git
2620
2621 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002622
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002623 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002624 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002625
Remy Böhmer1469c282020-12-15 18:49:02 +01002626 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002627 cur = self.bare_ref.symref(ref)
2628
2629 if self.revisionId:
2630 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2631 msg = 'manifest set to %s' % self.revisionId
2632 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002633 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002634 else:
2635 remote = self.GetRemote(self.remote.name)
2636 dst = remote.ToLocal(self.revisionExpr)
2637 if cur != dst:
2638 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002639 if detach:
2640 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2641 else:
2642 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002643
Kevin Degi384b3c52014-10-16 16:02:58 -06002644 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002645 # Git worktrees don't use symlinks to share at all.
2646 if self.use_git_worktrees:
2647 return
2648
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002649 symlink_files = self.shareable_files[:]
2650 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002651 if share_refs:
2652 symlink_files += self.working_tree_files
2653 symlink_dirs += self.working_tree_dirs
2654 to_symlink = symlink_files + symlink_dirs
2655 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002656 # Try to self-heal a bit in simple cases.
2657 dst_path = os.path.join(destdir, name)
2658 src_path = os.path.join(srcdir, name)
2659
2660 if name in self.working_tree_dirs:
2661 # If the dir is missing under .repo/projects/, create it.
2662 if not os.path.exists(src_path):
2663 os.makedirs(src_path)
2664
2665 elif name in self.working_tree_files:
2666 # If it's a file under the checkout .git/ and the .repo/projects/ has
2667 # nothing, move the file under the .repo/projects/ tree.
2668 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2669 platform_utils.rename(dst_path, src_path)
2670
2671 # If the path exists under the .repo/projects/ and there's no symlink
2672 # under the checkout .git/, recreate the symlink.
2673 if name in self.working_tree_dirs or name in self.working_tree_files:
2674 if os.path.exists(src_path) and not os.path.exists(dst_path):
2675 platform_utils.symlink(
2676 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2677
2678 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002679 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002680 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002681 # Fail if the links are pointing to the wrong place
2682 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002683 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002684 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002685 'work tree. If you\'re comfortable with the '
2686 'possibility of losing the work tree\'s git metadata,'
2687 ' use `repo sync --force-sync {0}` to '
2688 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002689
David James8d201162013-10-11 17:03:19 -07002690 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2691 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2692
2693 Args:
2694 gitdir: The bare git repository. Must already be initialized.
2695 dotgit: The repository you would like to initialize.
2696 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2697 Only one work tree can store refs under a given |gitdir|.
2698 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2699 This saves you the effort of initializing |dotgit| yourself.
2700 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002701 symlink_files = self.shareable_files[:]
2702 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002703 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002704 symlink_files += self.working_tree_files
2705 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002706 to_symlink = symlink_files + symlink_dirs
2707
2708 to_copy = []
2709 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002710 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002711
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002712 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002713 for name in set(to_copy).union(to_symlink):
2714 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002715 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002716 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002717
Kevin Degi384b3c52014-10-16 16:02:58 -06002718 if os.path.lexists(dst):
2719 continue
David James8d201162013-10-11 17:03:19 -07002720
2721 # If the source dir doesn't exist, create an empty dir.
2722 if name in symlink_dirs and not os.path.lexists(src):
2723 os.makedirs(src)
2724
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002725 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002726 platform_utils.symlink(
2727 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002728 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002729 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002730 shutil.copytree(src, dst)
2731 elif os.path.isfile(src):
2732 shutil.copy(src, dst)
2733
Conley Owens80b87fe2014-05-09 17:13:44 -07002734 # If the source file doesn't exist, ensure the destination
2735 # file doesn't either.
2736 if name in symlink_files and not os.path.lexists(src):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002737 platform_utils.remove(dst, missing_ok=True)
Conley Owens80b87fe2014-05-09 17:13:44 -07002738
David James8d201162013-10-11 17:03:19 -07002739 except OSError as e:
2740 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002741 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002742 else:
2743 raise
2744
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002745 def _InitGitWorktree(self):
2746 """Init the project using git worktrees."""
2747 self.bare_git.worktree('prune')
2748 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2749 self.worktree, self.GetRevisionId())
2750
2751 # Rewrite the internal state files to use relative paths between the
2752 # checkouts & worktrees.
2753 dotgit = os.path.join(self.worktree, '.git')
2754 with open(dotgit, 'r') as fp:
2755 # Figure out the checkout->worktree path.
2756 setting = fp.read()
2757 assert setting.startswith('gitdir:')
2758 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002759 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2760 # of file permissions. Delete it and recreate it from scratch to avoid.
2761 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002762 # Use relative path from checkout->worktree & maintain Unix line endings
2763 # on all OS's to match git behavior.
2764 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002765 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2766 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002767 # Use relative path from worktree->checkout & maintain Unix line endings
2768 # on all OS's to match git behavior.
2769 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002770 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2771
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002772 self._InitMRef()
2773
Martin Kellye4e94d22017-03-21 16:05:12 -07002774 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002775 realdotgit = os.path.join(self.worktree, '.git')
2776 tmpdotgit = realdotgit + '.tmp'
2777 init_dotgit = not os.path.exists(realdotgit)
2778 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002779 if self.use_git_worktrees:
2780 self._InitGitWorktree()
2781 self._CopyAndLinkFiles()
2782 return
2783
Mike Frysingerf4545122019-11-11 04:34:16 -05002784 dotgit = tmpdotgit
2785 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2786 os.makedirs(tmpdotgit)
2787 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2788 copy_all=False)
2789 else:
2790 dotgit = realdotgit
2791
Kevin Degib1a07b82015-07-27 13:33:43 -06002792 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002793 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2794 except GitError as e:
2795 if force_sync and not init_dotgit:
2796 try:
2797 platform_utils.rmtree(dotgit)
2798 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002799 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002800 raise e
2801 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002802
Mike Frysingerf4545122019-11-11 04:34:16 -05002803 if init_dotgit:
2804 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002805
Mike Frysingerf4545122019-11-11 04:34:16 -05002806 # Now that the .git dir is fully set up, move it to its final home.
2807 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808
Mike Frysingerf4545122019-11-11 04:34:16 -05002809 # Finish checking out the worktree.
2810 cmd = ['read-tree', '--reset', '-u']
2811 cmd.append('-v')
2812 cmd.append(HEAD)
2813 if GitCommand(self, cmd).Wait() != 0:
2814 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002815
Mike Frysingerf4545122019-11-11 04:34:16 -05002816 if submodules:
2817 self._SyncSubmodules(quiet=True)
2818 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002819
Renaud Paquay788e9622017-01-27 11:41:12 -08002820 def _get_symlink_error_message(self):
2821 if platform_utils.isWindows():
2822 return ('Unable to create symbolic link. Please re-run the command as '
2823 'Administrator, or see '
2824 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2825 'for other options.')
2826 return 'filesystem must support symlinks'
2827
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002829 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002830
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002831 def _revlist(self, *args, **kw):
2832 a = []
2833 a.extend(args)
2834 a.append('--')
2835 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836
2837 @property
2838 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002839 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002841 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002842 """Get logs between two revisions of this project."""
2843 comp = '..'
2844 if rev1:
2845 revs = [rev1]
2846 if rev2:
2847 revs.extend([comp, rev2])
2848 cmd = ['log', ''.join(revs)]
2849 out = DiffColoring(self.config)
2850 if out.is_on and color:
2851 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002852 if pretty_format is not None:
2853 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002854 if oneline:
2855 cmd.append('--oneline')
2856
2857 try:
2858 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2859 if log.Wait() == 0:
2860 return log.stdout
2861 except GitError:
2862 # worktree may not exist if groups changed for example. In that case,
2863 # try in gitdir instead.
2864 if not os.path.exists(self.worktree):
2865 return self.bare_git.log(*cmd[1:])
2866 else:
2867 raise
2868 return None
2869
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002870 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2871 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002872 """Get the list of logs from this revision to given revisionId"""
2873 logs = {}
2874 selfId = self.GetRevisionId(self._allrefs)
2875 toId = toProject.GetRevisionId(toProject._allrefs)
2876
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002877 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2878 pretty_format=pretty_format)
2879 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2880 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002881 return logs
2882
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002883 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002884
David James8d201162013-10-11 17:03:19 -07002885 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002886 self._project = project
2887 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002888 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002889
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002890 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2891 def __getstate__(self):
2892 return (self._project, self._bare, self._gitdir)
2893
2894 def __setstate__(self, state):
2895 self._project, self._bare, self._gitdir = state
2896
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002897 def LsOthers(self):
2898 p = GitCommand(self._project,
2899 ['ls-files',
2900 '-z',
2901 '--others',
2902 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002903 bare=False,
David James8d201162013-10-11 17:03:19 -07002904 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002905 capture_stdout=True,
2906 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002907 if p.Wait() == 0:
2908 out = p.stdout
2909 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002910 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002911 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002912 return []
2913
2914 def DiffZ(self, name, *args):
2915 cmd = [name]
2916 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002917 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002918 cmd.extend(args)
2919 p = GitCommand(self._project,
2920 cmd,
David James8d201162013-10-11 17:03:19 -07002921 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002922 bare=False,
2923 capture_stdout=True,
2924 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002925 p.Wait()
2926 r = {}
2927 out = p.stdout
2928 if out:
2929 out = iter(out[:-1].split('\0'))
2930 while out:
2931 try:
2932 info = next(out)
2933 path = next(out)
2934 except StopIteration:
2935 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002936
Mike Frysinger84230002021-02-16 17:08:35 -05002937 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002938
Mike Frysinger84230002021-02-16 17:08:35 -05002939 def __init__(self, path, omode, nmode, oid, nid, state):
2940 self.path = path
2941 self.src_path = None
2942 self.old_mode = omode
2943 self.new_mode = nmode
2944 self.old_id = oid
2945 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002946
Mike Frysinger84230002021-02-16 17:08:35 -05002947 if len(state) == 1:
2948 self.status = state
2949 self.level = None
2950 else:
2951 self.status = state[:1]
2952 self.level = state[1:]
2953 while self.level.startswith('0'):
2954 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002955
Mike Frysinger84230002021-02-16 17:08:35 -05002956 info = info[1:].split(' ')
2957 info = _Info(path, *info)
2958 if info.status in ('R', 'C'):
2959 info.src_path = info.path
2960 info.path = next(out)
2961 r[info.path] = info
2962 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002963
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002964 def GetDotgitPath(self, subpath=None):
2965 """Return the full path to the .git dir.
2966
2967 As a convenience, append |subpath| if provided.
2968 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002969 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002970 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002971 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002972 dotgit = os.path.join(self._project.worktree, '.git')
2973 if os.path.isfile(dotgit):
2974 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2975 with open(dotgit) as fp:
2976 setting = fp.read()
2977 assert setting.startswith('gitdir:')
2978 gitdir = setting.split(':', 1)[1].strip()
2979 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2980
2981 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2982
2983 def GetHead(self):
2984 """Return the ref that HEAD points to."""
2985 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002986 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002987 with open(path) as fd:
2988 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002989 except IOError as e:
2990 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002991 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302992 line = line.decode()
2993 except AttributeError:
2994 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002995 if line.startswith('ref: '):
2996 return line[5:-1]
2997 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002998
2999 def SetHead(self, ref, message=None):
3000 cmdv = []
3001 if message is not None:
3002 cmdv.extend(['-m', message])
3003 cmdv.append(HEAD)
3004 cmdv.append(ref)
3005 self.symbolic_ref(*cmdv)
3006
3007 def DetachHead(self, new, message=None):
3008 cmdv = ['--no-deref']
3009 if message is not None:
3010 cmdv.extend(['-m', message])
3011 cmdv.append(HEAD)
3012 cmdv.append(new)
3013 self.update_ref(*cmdv)
3014
3015 def UpdateRef(self, name, new, old=None,
3016 message=None,
3017 detach=False):
3018 cmdv = []
3019 if message is not None:
3020 cmdv.extend(['-m', message])
3021 if detach:
3022 cmdv.append('--no-deref')
3023 cmdv.append(name)
3024 cmdv.append(new)
3025 if old is not None:
3026 cmdv.append(old)
3027 self.update_ref(*cmdv)
3028
3029 def DeleteRef(self, name, old=None):
3030 if not old:
3031 old = self.rev_parse(name)
3032 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003033 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003034
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003035 def rev_list(self, *args, **kw):
3036 if 'format' in kw:
3037 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3038 else:
3039 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003040 cmdv.extend(args)
3041 p = GitCommand(self._project,
3042 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003043 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003044 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003045 capture_stdout=True,
3046 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003047 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003048 raise GitError('%s rev-list %s: %s' %
3049 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003050 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003051
3052 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003053 """Allow arbitrary git commands using pythonic syntax.
3054
3055 This allows you to do things like:
3056 git_obj.rev_parse('HEAD')
3057
3058 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3059 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003060 Any other positional arguments will be passed to the git command, and the
3061 following keyword arguments are supported:
3062 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003063
3064 Args:
3065 name: The name of the git command to call. Any '_' characters will
3066 be replaced with '-'.
3067
3068 Returns:
3069 A callable object that will try to call git with the named command.
3070 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003071 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003072
Dave Borowitz091f8932012-10-23 17:01:04 -07003073 def runner(*args, **kwargs):
3074 cmdv = []
3075 config = kwargs.pop('config', None)
3076 for k in kwargs:
3077 raise TypeError('%s() got an unexpected keyword argument %r'
3078 % (name, k))
3079 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303080 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003081 cmdv.append('-c')
3082 cmdv.append('%s=%s' % (k, v))
3083 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003084 cmdv.extend(args)
3085 p = GitCommand(self._project,
3086 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003087 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003088 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003089 capture_stdout=True,
3090 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003091 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003092 raise GitError('%s %s: %s' %
3093 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003094 r = p.stdout
3095 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3096 return r[:-1]
3097 return r
3098 return runner
3099
3100
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003101class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003102
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003103 def __str__(self):
3104 return 'prior sync failed; rebase still in progress'
3105
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003106
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003107class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003108
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003109 def __str__(self):
3110 return 'contains uncommitted changes'
3111
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003112
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003113class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003114
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003115 def __init__(self, project, text):
3116 self.project = project
3117 self.text = text
3118
3119 def Print(self, syncbuf):
3120 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3121 syncbuf.out.nl()
3122
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003123
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003124class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003125
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003126 def __init__(self, project, why):
3127 self.project = project
3128 self.why = why
3129
3130 def Print(self, syncbuf):
3131 syncbuf.out.fail('error: %s/: %s',
3132 self.project.relpath,
3133 str(self.why))
3134 syncbuf.out.nl()
3135
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003136
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003137class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003138
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003139 def __init__(self, project, action):
3140 self.project = project
3141 self.action = action
3142
3143 def Run(self, syncbuf):
3144 out = syncbuf.out
3145 out.project('project %s/', self.project.relpath)
3146 out.nl()
3147 try:
3148 self.action()
3149 out.nl()
3150 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003151 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003152 out.nl()
3153 return False
3154
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003155
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003156class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003157
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003158 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003159 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003160 self.project = self.printer('header', attr='bold')
3161 self.info = self.printer('info')
3162 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003163
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003164
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003165class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003166
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003167 def __init__(self, config, detach_head=False):
3168 self._messages = []
3169 self._failures = []
3170 self._later_queue1 = []
3171 self._later_queue2 = []
3172
3173 self.out = _SyncColoring(config)
3174 self.out.redirect(sys.stderr)
3175
3176 self.detach_head = detach_head
3177 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003178 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003179
3180 def info(self, project, fmt, *args):
3181 self._messages.append(_InfoMessage(project, fmt % args))
3182
3183 def fail(self, project, err=None):
3184 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003185 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003186
3187 def later1(self, project, what):
3188 self._later_queue1.append(_Later(project, what))
3189
3190 def later2(self, project, what):
3191 self._later_queue2.append(_Later(project, what))
3192
3193 def Finish(self):
3194 self._PrintMessages()
3195 self._RunLater()
3196 self._PrintMessages()
3197 return self.clean
3198
David Rileye0684ad2017-04-05 00:02:59 -07003199 def Recently(self):
3200 recent_clean = self.recent_clean
3201 self.recent_clean = True
3202 return recent_clean
3203
3204 def _MarkUnclean(self):
3205 self.clean = False
3206 self.recent_clean = False
3207
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003208 def _RunLater(self):
3209 for q in ['_later_queue1', '_later_queue2']:
3210 if not self._RunQueue(q):
3211 return
3212
3213 def _RunQueue(self, queue):
3214 for m in getattr(self, queue):
3215 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003216 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003217 return False
3218 setattr(self, queue, [])
3219 return True
3220
3221 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003222 if self._messages or self._failures:
3223 if os.isatty(2):
3224 self.out.write(progress.CSI_ERASE_LINE)
3225 self.out.write('\r')
3226
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003227 for m in self._messages:
3228 m.Print(self)
3229 for m in self._failures:
3230 m.Print(self)
3231
3232 self._messages = []
3233 self._failures = []
3234
3235
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003236class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003237
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003238 """A special project housed under .repo.
3239 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003240
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003241 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003242 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003243 manifest=manifest,
3244 name=name,
3245 gitdir=gitdir,
3246 objdir=gitdir,
3247 worktree=worktree,
3248 remote=RemoteSpec('origin'),
3249 relpath='.repo/%s' % name,
3250 revisionExpr='refs/heads/master',
3251 revisionId=None,
3252 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003253
3254 def PreSync(self):
3255 if self.Exists:
3256 cb = self.CurrentBranch
3257 if cb:
3258 base = self.GetBranch(cb).merge
3259 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003260 self.revisionExpr = base
3261 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003262
Martin Kelly224a31a2017-07-10 14:46:25 -07003263 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003264 """ Prepare MetaProject for manifest branch switch
3265 """
3266
3267 # detach and delete manifest branch, allowing a new
3268 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003269 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003270 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003271 syncbuf.Finish()
3272
3273 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003274 ['update-ref', '-d', 'refs/heads/default'],
3275 capture_stdout=True,
3276 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003277
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003278 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003279 def LastFetch(self):
3280 try:
3281 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3282 return os.path.getmtime(fh)
3283 except OSError:
3284 return 0
3285
3286 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003287 def HasChanges(self):
3288 """Has the remote received new commits not yet checked out?
3289 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003290 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003291 return False
3292
David Pursehouse8a68ff92012-09-24 12:15:13 +09003293 all_refs = self.bare_ref.all
3294 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003295 head = self.work_git.GetHead()
3296 if head.startswith(R_HEADS):
3297 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003298 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003299 except KeyError:
3300 head = None
3301
3302 if revid == head:
3303 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003304 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003305 return True
3306 return False