blob: 5b26b64c8fcedecab05b2efb2e3cddebfc773173 [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
Michael Kelly37c21c22020-06-13 02:10:40 -0700522 self.UpdatePaths(relpath, worktree, gitdir, objdir)
Michael Kelly2f3c3312020-07-21 19:40:38 -0700523 self.SetRevision(revisionExpr, revisionId=revisionId)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700524
Mike Pontillod3153822012-02-28 11:53:24 -0800525 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700526 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700527 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800528 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900529 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900530 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700531 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800532 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500533 # NB: Do not use this setting in __init__ to change behavior so that the
534 # manifest.git checkout can inspect & change it after instantiating. See
535 # the XmlManifest init code for more info.
536 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800537 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900538 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600539 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800540 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800541
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700542 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700543 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500544 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500545 self.annotations = []
Bryan Jacobsf609f912013-05-06 13:36:24 -0400546 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700547 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700548
Doug Anderson37282b42011-03-04 11:54:18 -0800549 # This will be filled in if a project is later identified to be the
550 # project containing repo hooks.
551 self.enabled_repo_hooks = []
552
Michael Kelly2f3c3312020-07-21 19:40:38 -0700553 def SetRevision(self, revisionExpr, revisionId=None):
554 """Set revisionId based on revision expression and id"""
555 self.revisionExpr = revisionExpr
556 if revisionId is None and revisionExpr and IsId(revisionExpr):
557 self.revisionId = self.revisionExpr
558 else:
559 self.revisionId = revisionId
560
Michael Kelly37c21c22020-06-13 02:10:40 -0700561 def UpdatePaths(self, relpath, worktree, gitdir, objdir):
562 """Update paths used by this project"""
563 self.gitdir = gitdir.replace('\\', '/')
564 self.objdir = objdir.replace('\\', '/')
565 if worktree:
566 self.worktree = os.path.normpath(worktree).replace('\\', '/')
567 else:
568 self.worktree = None
569 self.relpath = relpath
570
571 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
572 defaults=self.manifest.globalConfig)
573
574 if self.worktree:
575 self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir)
576 else:
577 self.work_git = None
578 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir)
579 self.bare_ref = GitRefs(self.gitdir)
580 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
581
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800583 def Derived(self):
584 return self.is_derived
585
586 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700587 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700588 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700589
590 @property
591 def CurrentBranch(self):
592 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400593
594 The branch name omits the 'refs/heads/' prefix.
595 None is returned if the project is on a detached HEAD, or if the work_git is
596 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700597 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400598 try:
599 b = self.work_git.GetHead()
600 except NoManifestException:
601 # If the local checkout is in a bad state, don't barf. Let the callers
602 # process this like the head is unreadable.
603 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700604 if b.startswith(R_HEADS):
605 return b[len(R_HEADS):]
606 return None
607
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700608 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500609 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
610 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
611 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200612
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700613 def IsDirty(self, consider_untracked=True):
614 """Is the working directory modified in some way?
615 """
616 self.work_git.update_index('-q',
617 '--unmerged',
618 '--ignore-missing',
619 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900620 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700621 return True
622 if self.work_git.DiffZ('diff-files'):
623 return True
624 if consider_untracked and self.work_git.LsOthers():
625 return True
626 return False
627
628 _userident_name = None
629 _userident_email = None
630
631 @property
632 def UserName(self):
633 """Obtain the user's personal name.
634 """
635 if self._userident_name is None:
636 self._LoadUserIdentity()
637 return self._userident_name
638
639 @property
640 def UserEmail(self):
641 """Obtain the user's email address. This is very likely
642 to be their Gerrit login.
643 """
644 if self._userident_email is None:
645 self._LoadUserIdentity()
646 return self._userident_email
647
648 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900649 u = self.bare_git.var('GIT_COMMITTER_IDENT')
650 m = re.compile("^(.*) <([^>]*)> ").match(u)
651 if m:
652 self._userident_name = m.group(1)
653 self._userident_email = m.group(2)
654 else:
655 self._userident_name = ''
656 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700657
658 def GetRemote(self, name):
659 """Get the configuration for a single remote.
660 """
661 return self.config.GetRemote(name)
662
663 def GetBranch(self, name):
664 """Get the configuration for a single branch.
665 """
666 return self.config.GetBranch(name)
667
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700668 def GetBranches(self):
669 """Get all existing local branches.
670 """
671 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900672 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700673 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700674
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530675 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700676 if name.startswith(R_HEADS):
677 name = name[len(R_HEADS):]
678 b = self.GetBranch(name)
679 b.current = name == current
680 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900681 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700682 heads[name] = b
683
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530684 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700685 if name.startswith(R_PUB):
686 name = name[len(R_PUB):]
687 b = heads.get(name)
688 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900689 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700690
691 return heads
692
Colin Cross5acde752012-03-28 20:15:45 -0700693 def MatchesGroups(self, manifest_groups):
694 """Returns true if the manifest groups specified at init should cause
695 this project to be synced.
696 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700697 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700698
699 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700700 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700701 manifest_groups: "-group1,group2"
702 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500703
704 The special manifest group "default" will match any project that
705 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700706 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500707 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700708 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700709 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500710 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700711
Conley Owens971de8e2012-04-16 10:36:08 -0700712 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700713 for group in expanded_manifest_groups:
714 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700715 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700716 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700717 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700718
Conley Owens971de8e2012-04-16 10:36:08 -0700719 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700720
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700721# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700722 def UncommitedFiles(self, get_all=True):
723 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700725 Args:
726 get_all: a boolean, if True - get information about all different
727 uncommitted files. If False - return as soon as any kind of
728 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500729 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700730 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500731 self.work_git.update_index('-q',
732 '--unmerged',
733 '--ignore-missing',
734 '--refresh')
735 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700736 details.append("rebase in progress")
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-index', '--cached', HEAD).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.DiffZ('diff-files').keys()
747 if changes:
748 details.extend(changes)
749 if not get_all:
750 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500751
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700752 changes = self.work_git.LsOthers()
753 if changes:
754 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500755
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700756 return details
757
758 def HasChanges(self):
759 """Returns true if there are uncommitted changes.
760 """
761 if self.UncommitedFiles(get_all=False):
762 return True
763 else:
764 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500765
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600766 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700767 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200768
769 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200770 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600771 quiet: If True then only print the project name. Do not print
772 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700774 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700775 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200776 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700777 print(file=output_redir)
778 print('project %s/' % self.relpath, file=output_redir)
779 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 return
781
782 self.work_git.update_index('-q',
783 '--unmerged',
784 '--ignore-missing',
785 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700786 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700787 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
788 df = self.work_git.DiffZ('diff-files')
789 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100790 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700791 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700792
793 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700794 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200795 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700796 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700797
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600798 if quiet:
799 out.nl()
800 return 'DIRTY'
801
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700802 branch = self.CurrentBranch
803 if branch is None:
804 out.nobranch('(*** NO BRANCH ***)')
805 else:
806 out.branch('branch %s', branch)
807 out.nl()
808
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700809 if rb:
810 out.important('prior sync failed; rebase still in progress')
811 out.nl()
812
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700813 paths = list()
814 paths.extend(di.keys())
815 paths.extend(df.keys())
816 paths.extend(do)
817
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530818 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900819 try:
820 i = di[p]
821 except KeyError:
822 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700823
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900824 try:
825 f = df[p]
826 except KeyError:
827 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200828
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900829 if i:
830 i_status = i.status.upper()
831 else:
832 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700833
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900834 if f:
835 f_status = f.status.lower()
836 else:
837 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700838
839 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800840 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700841 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700842 else:
843 line = ' %s%s\t%s' % (i_status, f_status, p)
844
845 if i and not f:
846 out.added('%s', line)
847 elif (i and f) or (not i and f):
848 out.changed('%s', line)
849 elif not i and not f:
850 out.untracked('%s', line)
851 else:
852 out.write('%s', line)
853 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200854
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700855 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700856
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500857 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858 """Prints the status of the repository to stdout.
859 """
860 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500861 if output_redir:
862 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863 cmd = ['diff']
864 if out.is_on:
865 cmd.append('--color')
866 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300867 if absolute_paths:
868 cmd.append('--src-prefix=a/%s/' % self.relpath)
869 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400871 try:
872 p = GitCommand(self,
873 cmd,
874 capture_stdout=True,
875 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500876 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400877 except GitError as e:
878 out.nl()
879 out.project('project %s/' % self.relpath)
880 out.nl()
881 out.fail('%s', str(e))
882 out.nl()
883 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500884 if p.stdout:
885 out.nl()
886 out.project('project %s/' % self.relpath)
887 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500888 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400889 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700890
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700891# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900892 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700893 """Was the branch published (uploaded) for code review?
894 If so, returns the SHA-1 hash of the last published
895 state for the branch.
896 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700897 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900898 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700899 try:
900 return self.bare_git.rev_parse(key)
901 except GitError:
902 return None
903 else:
904 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900905 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700906 except KeyError:
907 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908
David Pursehouse8a68ff92012-09-24 12:15:13 +0900909 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700910 """Prunes any stale published refs.
911 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900912 if all_refs is None:
913 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914 heads = set()
915 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530916 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917 if name.startswith(R_HEADS):
918 heads.add(name)
919 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900920 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700921
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530922 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923 n = name[len(R_PUB):]
924 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900925 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700927 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 """List any branches which can be uploaded for review.
929 """
930 heads = {}
931 pubed = {}
932
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530933 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700934 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900935 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900937 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938
939 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530940 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900941 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700943 if selected_branch and branch != selected_branch:
944 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800946 rb = self.GetUploadableBranch(branch)
947 if rb:
948 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700949 return ready
950
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800951 def GetUploadableBranch(self, branch_name):
952 """Get a single uploadable branch, or None.
953 """
954 branch = self.GetBranch(branch_name)
955 base = branch.LocalMerge
956 if branch.LocalMerge:
957 rb = ReviewableBranch(self, branch, base)
958 if rb.commits:
959 return rb
960 return None
961
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700962 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100963 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500964 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700965 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500966 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500967 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200968 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700969 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200970 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200971 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800972 validate_certs=True,
973 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700974 """Uploads the named branch for code review.
975 """
976 if branch is None:
977 branch = self.CurrentBranch
978 if branch is None:
979 raise GitError('not currently on a branch')
980
981 branch = self.GetBranch(branch)
982 if not branch.LocalMerge:
983 raise GitError('branch %s does not track a remote' % branch.name)
984 if not branch.remote.review:
985 raise GitError('remote %s has no review url' % branch.remote.name)
986
Bryan Jacobsf609f912013-05-06 13:36:24 -0400987 if dest_branch is None:
988 dest_branch = self.dest_branch
989 if dest_branch is None:
990 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700991 if not dest_branch.startswith(R_HEADS):
992 dest_branch = R_HEADS + dest_branch
993
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800994 if not branch.remote.projectname:
995 branch.remote.projectname = self.name
996 branch.remote.Save()
997
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200998 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800999 if url is None:
1000 raise UploadError('review not configured')
1001 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -05001002 if dryrun:
1003 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001004
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001005 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001006 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001007
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001008 for push_option in (push_options or []):
1009 cmd.append('-o')
1010 cmd.append(push_option)
1011
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001012 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001013
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001014 if dest_branch.startswith(R_HEADS):
1015 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001016
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001017 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001018 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001019 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001020 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001021 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001022 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001023
David Pursehousef25a3702018-11-14 19:01:22 -08001024 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001025 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001026 if notify:
1027 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001028 if private:
1029 opts += ['private']
1030 if wip:
1031 opts += ['wip']
1032 if opts:
1033 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001034 cmd.append(ref_spec)
1035
Anthony King7bdac712014-07-16 12:56:40 +01001036 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001037 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038
Mike Frysingerd7f86832020-11-19 19:18:46 -05001039 if not dryrun:
1040 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1041 self.bare_git.UpdateRef(R_PUB + branch.name,
1042 R_HEADS + branch.name,
1043 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001045# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001046 def _ExtractArchive(self, tarpath, path=None):
1047 """Extract the given tar on its current location
1048
1049 Args:
1050 - tarpath: The path to the actual tar file
1051
1052 """
1053 try:
1054 with tarfile.open(tarpath, 'r') as tar:
1055 tar.extractall(path=path)
1056 return True
1057 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001058 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001059 return False
1060
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001061 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001062 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001063 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001064 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001065 is_new=None,
Mike Frysinger73561142021-05-03 01:10:09 -04001066 current_branch_only=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001067 force_sync=False,
1068 clone_bundle=True,
Mike Frysingerd68ed632021-05-03 01:21:35 -04001069 tags=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001070 archive=False,
1071 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001072 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001073 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001074 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001075 ssh_proxy=None,
Raman Tennetif32f2432021-04-12 20:57:25 -07001076 clone_filter=None,
Raman Tenneticd89ec12021-04-22 09:18:14 -07001077 partial_clone_exclude=set()):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078 """Perform only the network IO portion of the sync process.
1079 Local working directory/branch state is not affected.
1080 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001081 if archive and not isinstance(self, MetaProject):
1082 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001083 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001084 return False
1085
1086 name = self.relpath.replace('\\', '/')
1087 name = name.replace('/', '_')
1088 tarpath = '%s.tar' % name
1089 topdir = self.manifest.topdir
1090
1091 try:
1092 self._FetchArchive(tarpath, cwd=topdir)
1093 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001094 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001095 return False
1096
1097 # From now on, we only need absolute tarpath
1098 tarpath = os.path.join(topdir, tarpath)
1099
1100 if not self._ExtractArchive(tarpath, path=topdir):
1101 return False
1102 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001103 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001104 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001105 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001106 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001107 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001108
1109 # If the shared object dir already exists, don't try to rebootstrap with a
1110 # clone bundle download. We should have the majority of objects already.
1111 if clone_bundle and os.path.exists(self.objdir):
1112 clone_bundle = False
1113
Raman Tennetif32f2432021-04-12 20:57:25 -07001114 if self.name in partial_clone_exclude:
1115 clone_bundle = True
1116 clone_filter = None
1117
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001118 if is_new is None:
1119 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001120 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001121 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001122 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001123 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001124 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001125
1126 if is_new:
1127 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1128 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001129 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001130 # This works for both absolute and relative alternate directories.
1131 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001132 except IOError:
1133 alt_dir = None
1134 else:
1135 alt_dir = None
1136
Mike Frysingere50b6a72020-02-19 01:45:48 -05001137 if (clone_bundle
1138 and alt_dir is None
1139 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001140 is_new = False
1141
Mike Frysinger73561142021-05-03 01:10:09 -04001142 if current_branch_only is None:
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001143 if self.sync_c:
1144 current_branch_only = True
1145 elif not self.manifest._loaded:
1146 # Manifest cannot check defaults until it syncs.
1147 current_branch_only = False
1148 elif self.manifest.default.sync_c:
1149 current_branch_only = True
1150
Mike Frysingerd68ed632021-05-03 01:21:35 -04001151 if tags is None:
1152 tags = self.sync_tags
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001153
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001154 if self.clone_depth:
1155 depth = self.clone_depth
1156 else:
1157 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1158
Mike Frysinger521d01b2020-02-17 01:51:49 -05001159 # See if we can skip the network fetch entirely.
1160 if not (optimized_fetch and
1161 (ID_RE.match(self.revisionExpr) and
1162 self._CheckForImmutableRevision())):
1163 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001164 initial=is_new,
1165 quiet=quiet, verbose=verbose, output_redir=output_redir,
1166 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001167 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001168 submodules=submodules, force_sync=force_sync,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001169 ssh_proxy=ssh_proxy,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001170 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001171 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001172
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001173 mp = self.manifest.manifestProject
1174 dissociate = mp.config.GetBoolean('repo.dissociate')
1175 if dissociate:
1176 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1177 if os.path.exists(alternates_file):
1178 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001179 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1180 merge_output=bool(output_redir))
1181 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001182 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001183 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001184 return False
1185 platform_utils.remove(alternates_file)
1186
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001187 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001188 self._InitMRef()
1189 else:
1190 self._InitMirrorHead()
Mike Frysinger9d96f582021-09-28 11:27:24 -04001191 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
1192 missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001193 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001194
1195 def PostRepoUpgrade(self):
1196 self._InitHooks()
1197
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001198 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001199 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001200 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001201 for copyfile in self.copyfiles:
1202 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001203 for linkfile in self.linkfiles:
1204 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001205
Julien Camperguedd654222014-01-09 16:21:37 +01001206 def GetCommitRevisionId(self):
1207 """Get revisionId of a commit.
1208
1209 Use this method instead of GetRevisionId to get the id of the commit rather
1210 than the id of the current git object (for example, a tag)
1211
1212 """
1213 if not self.revisionExpr.startswith(R_TAGS):
1214 return self.GetRevisionId(self._allrefs)
1215
1216 try:
1217 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1218 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001219 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1220 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001221
David Pursehouse8a68ff92012-09-24 12:15:13 +09001222 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001223 if self.revisionId:
1224 return self.revisionId
1225
1226 rem = self.GetRemote(self.remote.name)
1227 rev = rem.ToLocal(self.revisionExpr)
1228
David Pursehouse8a68ff92012-09-24 12:15:13 +09001229 if all_refs is not None and rev in all_refs:
1230 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001231
1232 try:
1233 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1234 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001235 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1236 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001237
Raman Tenneti6a872c92021-01-14 19:17:50 -08001238 def SetRevisionId(self, revisionId):
Xin Li0e776a52021-06-29 21:42:34 +00001239 if self.revisionExpr:
Xin Liaabf79d2021-04-29 01:50:38 -07001240 self.upstream = self.revisionExpr
1241
Raman Tenneti6a872c92021-01-14 19:17:50 -08001242 self.revisionId = revisionId
1243
Martin Kellye4e94d22017-03-21 16:05:12 -07001244 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001245 """Perform only the local IO portion of the sync process.
1246 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001247 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001248 if not os.path.exists(self.gitdir):
1249 syncbuf.fail(self,
1250 'Cannot checkout %s due to missing network sync; Run '
1251 '`repo sync -n %s` first.' %
1252 (self.name, self.name))
1253 return
1254
Martin Kellye4e94d22017-03-21 16:05:12 -07001255 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001256 all_refs = self.bare_ref.all
1257 self.CleanPublishedCache(all_refs)
1258 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001259
Mike Frysinger0458faa2021-03-10 23:35:44 -05001260 # Special case the root of the repo client checkout. Make sure it doesn't
1261 # contain files being checked out to dirs we don't allow.
1262 if self.relpath == '.':
1263 PROTECTED_PATHS = {'.repo'}
1264 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1265 bad_paths = paths & PROTECTED_PATHS
1266 if bad_paths:
1267 syncbuf.fail(self,
1268 'Refusing to checkout project that writes to protected '
1269 'paths: %s' % (', '.join(bad_paths),))
1270 return
1271
David Pursehouse1d947b32012-10-25 12:23:11 +09001272 def _doff():
1273 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001274 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001275
Martin Kellye4e94d22017-03-21 16:05:12 -07001276 def _dosubmodules():
1277 self._SyncSubmodules(quiet=True)
1278
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001279 head = self.work_git.GetHead()
1280 if head.startswith(R_HEADS):
1281 branch = head[len(R_HEADS):]
1282 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001283 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001284 except KeyError:
1285 head = None
1286 else:
1287 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001288
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001289 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001290 # Currently on a detached HEAD. The user is assumed to
1291 # not have any local modifications worth worrying about.
1292 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001293 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001294 syncbuf.fail(self, _PriorSyncFailedError())
1295 return
1296
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001297 if head == revid:
1298 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001299 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001300 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001301 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001302 # The copy/linkfile config may have changed.
1303 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001304 return
1305 else:
1306 lost = self._revlist(not_rev(revid), HEAD)
1307 if lost:
1308 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001309
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001310 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001311 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001312 if submodules:
1313 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001314 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001315 syncbuf.fail(self, e)
1316 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001317 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001318 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001319
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001320 if head == revid:
1321 # No changes; don't do anything further.
1322 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001323 # The copy/linkfile config may have changed.
1324 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001325 return
1326
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001327 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001328
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001329 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001330 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001331 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001332 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001333 syncbuf.info(self,
1334 "leaving %s; does not track upstream",
1335 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001336 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001337 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001338 if submodules:
1339 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001340 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001341 syncbuf.fail(self, e)
1342 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001343 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001344 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001345
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001346 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001347
1348 # See if we can perform a fast forward merge. This can happen if our
1349 # branch isn't in the exact same state as we last published.
1350 try:
1351 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1352 # Skip the published logic.
1353 pub = False
1354 except GitError:
1355 pub = self.WasPublished(branch.name, all_refs)
1356
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001357 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001358 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001359 if not_merged:
1360 if upstream_gain:
1361 # The user has published this branch and some of those
1362 # commits are not yet merged upstream. We do not want
1363 # to rewrite the published commits so we punt.
1364 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001365 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001366 "branch %s is published (but not merged) and is now "
1367 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001368 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001369 elif pub == head:
1370 # All published commits are merged, and thus we are a
1371 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001372 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001373 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001374 if submodules:
1375 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001376 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001377
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001378 # Examine the local commits not in the remote. Find the
1379 # last one attributed to this user, if any.
1380 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001381 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001382 last_mine = None
1383 cnt_mine = 0
1384 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001385 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001386 if committer_email == self.UserEmail:
1387 last_mine = commit_id
1388 cnt_mine += 1
1389
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001390 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001391 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001392
1393 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001394 syncbuf.fail(self, _DirtyError())
1395 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001396
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001397 # If the upstream switched on us, warn the user.
1398 #
1399 if branch.merge != self.revisionExpr:
1400 if branch.merge and self.revisionExpr:
1401 syncbuf.info(self,
1402 'manifest switched %s...%s',
1403 branch.merge,
1404 self.revisionExpr)
1405 elif branch.merge:
1406 syncbuf.info(self,
1407 'manifest no longer tracks %s',
1408 branch.merge)
1409
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001410 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001411 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001412 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001413 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001414 syncbuf.info(self,
1415 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001416 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001417
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001418 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001419 if not ID_RE.match(self.revisionExpr):
1420 # in case of manifest sync the revisionExpr might be a SHA1
1421 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001422 if not branch.merge.startswith('refs/'):
1423 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001424 branch.Save()
1425
Mike Pontillod3153822012-02-28 11:53:24 -08001426 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001427 def _docopyandlink():
1428 self._CopyAndLinkFiles()
1429
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001430 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001431 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001432 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001433 if submodules:
1434 syncbuf.later2(self, _dosubmodules)
1435 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001436 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001437 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001438 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001439 if submodules:
1440 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001441 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001442 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001443 syncbuf.fail(self, e)
1444 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001446 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001447 if submodules:
1448 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001449
Mike Frysingere6a202f2019-08-02 15:57:57 -04001450 def AddCopyFile(self, src, dest, topdir):
1451 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001452
Mike Frysingere6a202f2019-08-02 15:57:57 -04001453 No filesystem changes occur here. Actual copying happens later on.
1454
1455 Paths should have basic validation run on them before being queued.
1456 Further checking will be handled when the actual copy happens.
1457 """
1458 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1459
1460 def AddLinkFile(self, src, dest, topdir):
1461 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1462
1463 No filesystem changes occur here. Actual linking happens later on.
1464
1465 Paths should have basic validation run on them before being queued.
1466 Further checking will be handled when the actual link happens.
1467 """
1468 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001469
James W. Mills24c13082012-04-12 15:04:13 -05001470 def AddAnnotation(self, name, value, keep):
Jack Neus6ea0cae2021-07-20 20:52:33 +00001471 self.annotations.append(Annotation(name, value, keep))
James W. Mills24c13082012-04-12 15:04:13 -05001472
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001473 def DownloadPatchSet(self, change_id, patch_id):
1474 """Download a single patch set of a single change to FETCH_HEAD.
1475 """
1476 remote = self.GetRemote(self.remote.name)
1477
1478 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001479 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001480 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001481 if GitCommand(self, cmd, bare=True).Wait() != 0:
1482 return None
1483 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001484 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001485 change_id,
1486 patch_id,
1487 self.bare_git.rev_parse('FETCH_HEAD'))
1488
Mike Frysingerc0d18662020-02-19 19:19:18 -05001489 def DeleteWorktree(self, quiet=False, force=False):
1490 """Delete the source checkout and any other housekeeping tasks.
1491
1492 This currently leaves behind the internal .repo/ cache state. This helps
1493 when switching branches or manifest changes get reverted as we don't have
1494 to redownload all the git objects. But we should do some GC at some point.
1495
1496 Args:
1497 quiet: Whether to hide normal messages.
1498 force: Always delete tree even if dirty.
1499
1500 Returns:
1501 True if the worktree was completely cleaned out.
1502 """
1503 if self.IsDirty():
1504 if force:
1505 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1506 (self.relpath,), file=sys.stderr)
1507 else:
1508 print('error: %s: Cannot remove project: uncommitted changes are '
1509 'present.\n' % (self.relpath,), file=sys.stderr)
1510 return False
1511
1512 if not quiet:
1513 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1514
1515 # Unlock and delink from the main worktree. We don't use git's worktree
1516 # remove because it will recursively delete projects -- we handle that
1517 # ourselves below. https://crbug.com/git/48
1518 if self.use_git_worktrees:
1519 needle = platform_utils.realpath(self.gitdir)
1520 # Find the git worktree commondir under .repo/worktrees/.
1521 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1522 assert output.startswith('worktree '), output
1523 commondir = output[9:]
1524 # Walk each of the git worktrees to see where they point.
1525 configs = os.path.join(commondir, 'worktrees')
1526 for name in os.listdir(configs):
1527 gitdir = os.path.join(configs, name, 'gitdir')
1528 with open(gitdir) as fp:
1529 relpath = fp.read().strip()
1530 # Resolve the checkout path and see if it matches this project.
1531 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1532 if fullpath == needle:
1533 platform_utils.rmtree(os.path.join(configs, name))
1534
1535 # Delete the .git directory first, so we're less likely to have a partially
1536 # working git repository around. There shouldn't be any git projects here,
1537 # so rmtree works.
1538
1539 # Try to remove plain files first in case of git worktrees. If this fails
1540 # for any reason, we'll fall back to rmtree, and that'll display errors if
1541 # it can't remove things either.
1542 try:
1543 platform_utils.remove(self.gitdir)
1544 except OSError:
1545 pass
1546 try:
1547 platform_utils.rmtree(self.gitdir)
1548 except OSError as e:
1549 if e.errno != errno.ENOENT:
1550 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1551 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1552 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1553 return False
1554
1555 # Delete everything under the worktree, except for directories that contain
1556 # another git project.
1557 dirs_to_remove = []
1558 failed = False
1559 for root, dirs, files in platform_utils.walk(self.worktree):
1560 for f in files:
1561 path = os.path.join(root, f)
1562 try:
1563 platform_utils.remove(path)
1564 except OSError as e:
1565 if e.errno != errno.ENOENT:
1566 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1567 failed = True
1568 dirs[:] = [d for d in dirs
1569 if not os.path.lexists(os.path.join(root, d, '.git'))]
1570 dirs_to_remove += [os.path.join(root, d) for d in dirs
1571 if os.path.join(root, d) not in dirs_to_remove]
1572 for d in reversed(dirs_to_remove):
1573 if platform_utils.islink(d):
1574 try:
1575 platform_utils.remove(d)
1576 except OSError as e:
1577 if e.errno != errno.ENOENT:
1578 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1579 failed = True
1580 elif not platform_utils.listdir(d):
1581 try:
1582 platform_utils.rmdir(d)
1583 except OSError as e:
1584 if e.errno != errno.ENOENT:
1585 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1586 failed = True
1587 if failed:
1588 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1589 file=sys.stderr)
1590 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1591 return False
1592
1593 # Try deleting parent dirs if they are empty.
1594 path = self.worktree
1595 while path != self.manifest.topdir:
1596 try:
1597 platform_utils.rmdir(path)
1598 except OSError as e:
1599 if e.errno != errno.ENOENT:
1600 break
1601 path = os.path.dirname(path)
1602
1603 return True
1604
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001605# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001606 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001607 """Create a new branch off the manifest's revision.
1608 """
Simran Basib9a1b732015-08-20 12:19:28 -07001609 if not branch_merge:
1610 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001611 head = self.work_git.GetHead()
1612 if head == (R_HEADS + name):
1613 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001614
David Pursehouse8a68ff92012-09-24 12:15:13 +09001615 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001616 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001617 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001618 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001619 capture_stdout=True,
1620 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001621
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001622 branch = self.GetBranch(name)
1623 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001624 branch.merge = branch_merge
1625 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1626 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001627
1628 if revision is None:
1629 revid = self.GetRevisionId(all_refs)
1630 else:
1631 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001632
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001633 if head.startswith(R_HEADS):
1634 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001635 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001636 except KeyError:
1637 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001638 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001639 ref = R_HEADS + name
1640 self.work_git.update_ref(ref, revid)
1641 self.work_git.symbolic_ref(HEAD, ref)
1642 branch.Save()
1643 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001644
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001645 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001646 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001647 capture_stdout=True,
1648 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001649 branch.Save()
1650 return True
1651 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001652
Wink Saville02d79452009-04-10 13:01:24 -07001653 def CheckoutBranch(self, name):
1654 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001655
1656 Args:
1657 name: The name of the branch to checkout.
1658
1659 Returns:
1660 True if the checkout succeeded; False if it didn't; None if the branch
1661 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001662 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001663 rev = R_HEADS + name
1664 head = self.work_git.GetHead()
1665 if head == rev:
1666 # Already on the branch
1667 #
1668 return True
Wink Saville02d79452009-04-10 13:01:24 -07001669
David Pursehouse8a68ff92012-09-24 12:15:13 +09001670 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001671 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001672 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001673 except KeyError:
1674 # Branch does not exist in this project
1675 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001676 return None
Wink Saville02d79452009-04-10 13:01:24 -07001677
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001678 if head.startswith(R_HEADS):
1679 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001680 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001681 except KeyError:
1682 head = None
1683
1684 if head == revid:
1685 # Same revision; just update HEAD to point to the new
1686 # target branch, but otherwise take no other action.
1687 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001688 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1689 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001690 return True
1691
1692 return GitCommand(self,
1693 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001694 capture_stdout=True,
1695 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001696
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001697 def AbandonBranch(self, name):
1698 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001699
1700 Args:
1701 name: The name of the branch to abandon.
1702
1703 Returns:
1704 True if the abandon succeeded; False if it didn't; None if the branch
1705 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001706 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001707 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001708 all_refs = self.bare_ref.all
1709 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001710 # Doesn't exist
1711 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001712
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001713 head = self.work_git.GetHead()
1714 if head == rev:
1715 # We can't destroy the branch while we are sitting
1716 # on it. Switch to a detached HEAD.
1717 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001718 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001719
David Pursehouse8a68ff92012-09-24 12:15:13 +09001720 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001721 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001722 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001723 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001724 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001725
1726 return GitCommand(self,
1727 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001728 capture_stdout=True,
1729 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001730
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001731 def PruneHeads(self):
1732 """Prune any topic branches already merged into upstream.
1733 """
1734 cb = self.CurrentBranch
1735 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001736 left = self._allrefs
1737 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001738 if name.startswith(R_HEADS):
1739 name = name[len(R_HEADS):]
1740 if cb is None or name != cb:
1741 kill.append(name)
1742
Mike Frysingera3794e92021-03-11 23:24:01 -05001743 # Minor optimization: If there's nothing to prune, then don't try to read
1744 # any project state.
1745 if not kill and not cb:
1746 return []
1747
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001748 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001749 if cb is not None \
1750 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001751 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001752 self.work_git.DetachHead(HEAD)
1753 kill.append(cb)
1754
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001755 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001756 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001757
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758 try:
1759 self.bare_git.DetachHead(rev)
1760
1761 b = ['branch', '-d']
1762 b.extend(kill)
1763 b = GitCommand(self, b, bare=True,
1764 capture_stdout=True,
1765 capture_stderr=True)
1766 b.Wait()
1767 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001768 if ID_RE.match(old):
1769 self.bare_git.DetachHead(old)
1770 else:
1771 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001772 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001773
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001774 for branch in kill:
1775 if (R_HEADS + branch) not in left:
1776 self.CleanPublishedCache()
1777 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001778
1779 if cb and cb not in kill:
1780 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001781 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001782
1783 kept = []
1784 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001785 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001786 branch = self.GetBranch(branch)
1787 base = branch.LocalMerge
1788 if not base:
1789 base = rev
1790 kept.append(ReviewableBranch(self, branch, base))
1791 return kept
1792
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001793# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001794 def GetRegisteredSubprojects(self):
1795 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001796
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001797 def rec(subprojects):
1798 if not subprojects:
1799 return
1800 result.extend(subprojects)
1801 for p in subprojects:
1802 rec(p.subprojects)
1803 rec(self.subprojects)
1804 return result
1805
1806 def _GetSubmodules(self):
1807 # Unfortunately we cannot call `git submodule status --recursive` here
1808 # because the working tree might not exist yet, and it cannot be used
1809 # without a working tree in its current implementation.
1810
1811 def get_submodules(gitdir, rev):
1812 # Parse .gitmodules for submodule sub_paths and sub_urls
1813 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1814 if not sub_paths:
1815 return []
1816 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1817 # revision of submodule repository
1818 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1819 submodules = []
1820 for sub_path, sub_url in zip(sub_paths, sub_urls):
1821 try:
1822 sub_rev = sub_revs[sub_path]
1823 except KeyError:
1824 # Ignore non-exist submodules
1825 continue
1826 submodules.append((sub_rev, sub_path, sub_url))
1827 return submodules
1828
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001829 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1830 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001831
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001832 def parse_gitmodules(gitdir, rev):
1833 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1834 try:
Anthony King7bdac712014-07-16 12:56:40 +01001835 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1836 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001837 except GitError:
1838 return [], []
1839 if p.Wait() != 0:
1840 return [], []
1841
1842 gitmodules_lines = []
1843 fd, temp_gitmodules_path = tempfile.mkstemp()
1844 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001845 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001846 os.close(fd)
1847 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001848 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1849 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001850 if p.Wait() != 0:
1851 return [], []
1852 gitmodules_lines = p.stdout.split('\n')
1853 except GitError:
1854 return [], []
1855 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001856 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001857
1858 names = set()
1859 paths = {}
1860 urls = {}
1861 for line in gitmodules_lines:
1862 if not line:
1863 continue
1864 m = re_path.match(line)
1865 if m:
1866 names.add(m.group(1))
1867 paths[m.group(1)] = m.group(2)
1868 continue
1869 m = re_url.match(line)
1870 if m:
1871 names.add(m.group(1))
1872 urls[m.group(1)] = m.group(2)
1873 continue
1874 names = sorted(names)
1875 return ([paths.get(name, '') for name in names],
1876 [urls.get(name, '') for name in names])
1877
1878 def git_ls_tree(gitdir, rev, paths):
1879 cmd = ['ls-tree', rev, '--']
1880 cmd.extend(paths)
1881 try:
Anthony King7bdac712014-07-16 12:56:40 +01001882 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1883 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001884 except GitError:
1885 return []
1886 if p.Wait() != 0:
1887 return []
1888 objects = {}
1889 for line in p.stdout.split('\n'):
1890 if not line.strip():
1891 continue
1892 object_rev, object_path = line.split()[2:4]
1893 objects[object_path] = object_rev
1894 return objects
1895
1896 try:
1897 rev = self.GetRevisionId()
1898 except GitError:
1899 return []
1900 return get_submodules(self.gitdir, rev)
1901
1902 def GetDerivedSubprojects(self):
1903 result = []
1904 if not self.Exists:
1905 # If git repo does not exist yet, querying its submodules will
1906 # mess up its states; so return here.
1907 return result
1908 for rev, path, url in self._GetSubmodules():
1909 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001910 relpath, worktree, gitdir, objdir = \
1911 self.manifest.GetSubprojectPaths(self, name, path)
1912 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001913 if project:
1914 result.extend(project.GetDerivedSubprojects())
1915 continue
David James8d201162013-10-11 17:03:19 -07001916
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001917 if url.startswith('..'):
1918 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001919 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001920 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001921 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001922 review=self.remote.review,
1923 revision=self.remote.revision)
1924 subproject = Project(manifest=self.manifest,
1925 name=name,
1926 remote=remote,
1927 gitdir=gitdir,
1928 objdir=objdir,
1929 worktree=worktree,
1930 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001931 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001932 revisionId=rev,
1933 rebase=self.rebase,
1934 groups=self.groups,
1935 sync_c=self.sync_c,
1936 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001937 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001938 parent=self,
1939 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001940 result.append(subproject)
1941 result.extend(subproject.GetDerivedSubprojects())
1942 return result
1943
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001944# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001945 def EnableRepositoryExtension(self, key, value='true', version=1):
1946 """Enable git repository extension |key| with |value|.
1947
1948 Args:
1949 key: The extension to enabled. Omit the "extensions." prefix.
1950 value: The value to use for the extension.
1951 version: The minimum git repository version needed.
1952 """
1953 # Make sure the git repo version is new enough already.
1954 found_version = self.config.GetInt('core.repositoryFormatVersion')
1955 if found_version is None:
1956 found_version = 0
1957 if found_version < version:
1958 self.config.SetString('core.repositoryFormatVersion', str(version))
1959
1960 # Enable the extension!
1961 self.config.SetString('extensions.%s' % (key,), value)
1962
Mike Frysinger50a81de2020-09-06 15:51:21 -04001963 def ResolveRemoteHead(self, name=None):
1964 """Find out what the default branch (HEAD) points to.
1965
1966 Normally this points to refs/heads/master, but projects are moving to main.
1967 Support whatever the server uses rather than hardcoding "master" ourselves.
1968 """
1969 if name is None:
1970 name = self.remote.name
1971
1972 # The output will look like (NB: tabs are separators):
1973 # ref: refs/heads/master HEAD
1974 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1975 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1976
1977 for line in output.splitlines():
1978 lhs, rhs = line.split('\t', 1)
1979 if rhs == 'HEAD' and lhs.startswith('ref:'):
1980 return lhs[4:].strip()
1981
1982 return None
1983
Zac Livingstone4332262017-06-16 08:56:09 -06001984 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001985 try:
1986 # if revision (sha or tag) is not present then following function
1987 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001988 self.bare_git.rev_list('-1', '--missing=allow-any',
1989 '%s^0' % self.revisionExpr, '--')
Xin Li0e776a52021-06-29 21:42:34 +00001990 if self.upstream:
1991 rev = self.GetRemote(self.remote.name).ToLocal(self.upstream)
1992 self.bare_git.rev_list('-1', '--missing=allow-any',
1993 '%s^0' % rev, '--')
Xin Li8e983bb2021-07-20 20:15:30 +00001994 self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev)
Chris AtLee2fb64662014-01-16 21:32:33 -05001995 return True
1996 except GitError:
1997 # There is no such persistent revision. We have to fetch it.
1998 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001999
Julien Campergue335f5ef2013-10-16 11:02:35 +02002000 def _FetchArchive(self, tarpath, cwd=None):
2001 cmd = ['archive', '-v', '-o', tarpath]
2002 cmd.append('--remote=%s' % self.remote.url)
2003 cmd.append('--prefix=%s/' % self.relpath)
2004 cmd.append(self.revisionExpr)
2005
2006 command = GitCommand(self, cmd, cwd=cwd,
2007 capture_stdout=True,
2008 capture_stderr=True)
2009
2010 if command.Wait() != 0:
2011 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2012
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002013 def _RemoteFetch(self, name=None,
2014 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002015 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002016 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002017 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002018 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002019 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002020 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002021 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002022 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002023 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002024 ssh_proxy=None,
Xin Li745be2e2019-06-03 11:24:30 -07002025 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002026 clone_filter=None,
2027 retry_fetches=2,
2028 retry_sleep_initial_sec=4.0,
2029 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002030 is_sha1 = False
2031 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002032 # The depth should not be used when fetching to a mirror because
2033 # it will result in a shallow repository that cannot be cloned or
2034 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002035 # The repo project should also never be synced with partial depth.
2036 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2037 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002038
Shawn Pearce69e04d82014-01-29 12:48:54 -08002039 if depth:
2040 current_branch_only = True
2041
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002042 if ID_RE.match(self.revisionExpr) is not None:
2043 is_sha1 = True
2044
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002045 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002046 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002047 # this is a tag and its sha1 value should never change
2048 tag_name = self.revisionExpr[len(R_TAGS):]
2049
2050 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002051 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002052 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002053 print('Skipped fetching project %s (already have persistent ref)'
2054 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002055 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002056 if is_sha1 and not depth:
2057 # When syncing a specific commit and --depth is not set:
2058 # * if upstream is explicitly specified and is not a sha1, fetch only
2059 # upstream as users expect only upstream to be fetch.
2060 # Note: The commit might not be in upstream in which case the sync
2061 # will fail.
2062 # * otherwise, fetch all branches to make sure we end up with the
2063 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002064 if self.upstream:
2065 current_branch_only = not ID_RE.match(self.upstream)
2066 else:
2067 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002068
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002069 if not name:
2070 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002071
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002072 remote = self.GetRemote(name)
Mike Frysinger339f2df2021-05-06 00:44:42 -04002073 if not remote.PreConnectFetch(ssh_proxy):
2074 ssh_proxy = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002075
Shawn O. Pearce88443382010-10-08 10:02:09 +02002076 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002077 if alt_dir and 'objects' == os.path.basename(alt_dir):
2078 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002079 packed_refs = os.path.join(self.gitdir, 'packed-refs')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002080
David Pursehouse8a68ff92012-09-24 12:15:13 +09002081 all_refs = self.bare_ref.all
2082 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002083 tmp = set()
2084
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302085 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002086 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002087 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002088 all_refs[r] = ref_id
2089 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002090 continue
2091
David Pursehouse8a68ff92012-09-24 12:15:13 +09002092 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002093 continue
2094
David Pursehouse8a68ff92012-09-24 12:15:13 +09002095 r = 'refs/_alt/%s' % ref_id
2096 all_refs[r] = ref_id
2097 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002098 tmp.add(r)
2099
heping3d7bbc92017-04-12 19:51:47 +08002100 tmp_packed_lines = []
2101 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002102
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302103 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002104 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002105 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002107 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002108
heping3d7bbc92017-04-12 19:51:47 +08002109 tmp_packed = ''.join(tmp_packed_lines)
2110 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002112 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002113 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002114
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002115 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002116
Xin Li745be2e2019-06-03 11:24:30 -07002117 if clone_filter:
2118 git_require((2, 19, 0), fail=True, msg='partial clones')
2119 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002120 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002121
Conley Owensf97e8382015-01-21 11:12:46 -08002122 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002123 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002124 else:
2125 # If this repo has shallow objects, then we don't know which refs have
2126 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2127 # do this with projects that don't have shallow objects, since it is less
2128 # efficient.
2129 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2130 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002131
Mike Frysinger4847e052020-02-22 00:07:35 -05002132 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002133 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002134 if not quiet and sys.stdout.isatty():
2135 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002136 if not self.worktree:
2137 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002138 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002139
Mike Frysingere57f1142019-03-18 21:27:54 -04002140 if force_sync:
2141 cmd.append('--force')
2142
David Pursehouse74cfd272015-10-14 10:50:15 +09002143 if prune:
2144 cmd.append('--prune')
2145
Martin Kellye4e94d22017-03-21 16:05:12 -07002146 if submodules:
2147 cmd.append('--recurse-submodules=on-demand')
2148
Kuang-che Wu6856f982019-11-25 12:37:55 +08002149 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002150 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002151 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002152 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002153 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002154 spec.append('tag')
2155 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002156
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302157 if self.manifest.IsMirror and not current_branch_only:
2158 branch = None
2159 else:
2160 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002161 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002162 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002163 # Shallow checkout of a specific commit, fetch from that commit and not
2164 # the heads only as the commit might be deeper in the history.
2165 spec.append(branch)
Xin Liaabf79d2021-04-29 01:50:38 -07002166 if self.upstream:
2167 spec.append(self.upstream)
Kuang-che Wu6856f982019-11-25 12:37:55 +08002168 else:
2169 if is_sha1:
2170 branch = self.upstream
2171 if branch is not None and branch.strip():
2172 if not branch.startswith('refs/'):
2173 branch = R_HEADS + branch
2174 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2175
2176 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2177 # whole repo.
2178 if self.manifest.IsMirror and not spec:
2179 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2180
2181 # If using depth then we should not get all the tags since they may
2182 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002183 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002184 cmd.append('--no-tags')
2185 else:
2186 cmd.append('--tags')
2187 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2188
Conley Owens80b87fe2014-05-09 17:13:44 -07002189 cmd.extend(spec)
2190
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002191 # At least one retry minimum due to git remote prune.
2192 retry_fetches = max(retry_fetches, 2)
2193 retry_cur_sleep = retry_sleep_initial_sec
2194 ok = prune_tried = False
2195 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002196 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002197 merge_output=True, capture_stdout=quiet or bool(output_redir))
2198 if gitcmd.stdout and not quiet and output_redir:
2199 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002200 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002201 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002202 ok = True
2203 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002204
2205 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002206 elif (gitcmd.stdout and
2207 'error:' in gitcmd.stdout and
2208 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002209 # Fallthru to sleep+retry logic at the bottom.
2210 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002211
Mike Frysinger6823bc22021-04-15 02:06:28 -04002212 # Try to prune remote branches once in case there are conflicts.
2213 # For example, if the remote had refs/heads/upstream, but deleted that and
2214 # now has refs/heads/upstream/foo.
2215 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002216 'error:' in gitcmd.stdout and
2217 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002218 not prune_tried):
2219 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002220 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002221 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002222 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002223 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002224 break
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002225 print('retrying fetch after pruning remote branches', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002226 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002227 continue
Brian Harring14a66742012-09-28 20:21:57 -07002228 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002229 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2230 # in sha1 mode, we just tried sync'ing from the upstream field; it
2231 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002232 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002233 elif ret < 0:
2234 # Git died with a signal, exit immediately
2235 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002236
2237 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002238 if not verbose and gitcmd.stdout:
2239 print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002240 if try_n < retry_fetches - 1:
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002241 print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep),
2242 file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002243 time.sleep(retry_cur_sleep)
2244 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2245 MAXIMUM_RETRY_SLEEP_SEC)
2246 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2247 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002248
2249 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002250 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002251 if old_packed != '':
2252 _lwrite(packed_refs, old_packed)
2253 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002254 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002255 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002256
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002257 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002258 # We just synced the upstream given branch; verify we
2259 # got what we wanted, else trigger a second run of all
2260 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002261 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002262 # Sync the current branch only with depth set to None.
2263 # We always pass depth=None down to avoid infinite recursion.
2264 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002265 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002266 current_branch_only=current_branch_only and depth,
2267 initial=False, alt_dir=alt_dir,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002268 depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002269
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002270 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002271
Mike Frysingere50b6a72020-02-19 01:45:48 -05002272 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002273 if initial and \
2274 (self.manifest.manifestProject.config.GetString('repo.depth') or
2275 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002276 return False
2277
2278 remote = self.GetRemote(self.remote.name)
2279 bundle_url = remote.url + '/clone.bundle'
2280 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002281 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2282 'persistent-http',
2283 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002284 return False
2285
2286 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2287 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2288
2289 exist_dst = os.path.exists(bundle_dst)
2290 exist_tmp = os.path.exists(bundle_tmp)
2291
2292 if not initial and not exist_dst and not exist_tmp:
2293 return False
2294
2295 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002296 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2297 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002298 if not exist_dst:
2299 return False
2300
2301 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002302 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002303 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002304 if not quiet and sys.stdout.isatty():
2305 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002306 if not self.worktree:
2307 cmd.append('--update-head-ok')
2308 cmd.append(bundle_dst)
2309 for f in remote.fetch:
2310 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002311 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002312
2313 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Mike Frysinger9d96f582021-09-28 11:27:24 -04002314 platform_utils.remove(bundle_dst, missing_ok=True)
2315 platform_utils.remove(bundle_tmp, missing_ok=True)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002316 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002317
Mike Frysingere50b6a72020-02-19 01:45:48 -05002318 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002319 platform_utils.remove(dstPath, missing_ok=True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002320
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002321 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002322 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002323 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002324 if os.path.exists(tmpPath):
2325 size = os.stat(tmpPath).st_size
2326 if size >= 1024:
2327 cmd += ['--continue-at', '%d' % (size,)]
2328 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002329 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002330 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002331 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002332 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002333 if proxy:
2334 cmd += ['--proxy', proxy]
2335 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2336 cmd += ['--proxy', os.environ['http_proxy']]
2337 if srcUrl.startswith('persistent-https'):
2338 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2339 elif srcUrl.startswith('persistent-http'):
2340 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002341 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002342
Dave Borowitz137d0132015-01-02 11:12:54 -08002343 if IsTrace():
2344 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002345 if verbose:
2346 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2347 stdout = None if verbose else subprocess.PIPE
2348 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002349 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002350 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002351 except OSError:
2352 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002353
Mike Frysingere50b6a72020-02-19 01:45:48 -05002354 (output, _) = proc.communicate()
2355 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002356
Dave Borowitz137d0132015-01-02 11:12:54 -08002357 if curlret == 22:
2358 # From curl man page:
2359 # 22: HTTP page not retrieved. The requested url was not found or
2360 # returned another error with the HTTP error code being 400 or above.
2361 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002362 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002363 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2364 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002365 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002366 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002367 elif curlret and not verbose and output:
2368 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002369
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002370 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002371 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002372 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002373 return True
2374 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002375 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002376 return False
2377 else:
2378 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002379
Kris Giesingc8d882a2014-12-23 13:02:32 -08002380 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002381 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002382 with open(path, 'rb') as f:
2383 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002384 return True
2385 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002386 if not quiet:
2387 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002388 return False
2389 except OSError:
2390 return False
2391
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002392 def _Checkout(self, rev, quiet=False):
2393 cmd = ['checkout']
2394 if quiet:
2395 cmd.append('-q')
2396 cmd.append(rev)
2397 cmd.append('--')
2398 if GitCommand(self, cmd).Wait() != 0:
2399 if self._allrefs:
2400 raise GitError('%s checkout %s ' % (self.name, rev))
2401
Mike Frysinger915fda12020-03-22 12:15:20 -04002402 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002403 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002404 if ffonly:
2405 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002406 if record_origin:
2407 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002408 cmd.append(rev)
2409 cmd.append('--')
2410 if GitCommand(self, cmd).Wait() != 0:
2411 if self._allrefs:
2412 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2413
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302414 def _LsRemote(self, refs):
2415 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302416 p = GitCommand(self, cmd, capture_stdout=True)
2417 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002418 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302419 return None
2420
Anthony King7bdac712014-07-16 12:56:40 +01002421 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002422 cmd = ['revert']
2423 cmd.append('--no-edit')
2424 cmd.append(rev)
2425 cmd.append('--')
2426 if GitCommand(self, cmd).Wait() != 0:
2427 if self._allrefs:
2428 raise GitError('%s revert %s ' % (self.name, rev))
2429
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002430 def _ResetHard(self, rev, quiet=True):
2431 cmd = ['reset', '--hard']
2432 if quiet:
2433 cmd.append('-q')
2434 cmd.append(rev)
2435 if GitCommand(self, cmd).Wait() != 0:
2436 raise GitError('%s reset --hard %s ' % (self.name, rev))
2437
Martin Kellye4e94d22017-03-21 16:05:12 -07002438 def _SyncSubmodules(self, quiet=True):
2439 cmd = ['submodule', 'update', '--init', '--recursive']
2440 if quiet:
2441 cmd.append('-q')
2442 if GitCommand(self, cmd).Wait() != 0:
2443 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2444
Anthony King7bdac712014-07-16 12:56:40 +01002445 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002446 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002447 if onto is not None:
2448 cmd.extend(['--onto', onto])
2449 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002450 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002451 raise GitError('%s rebase %s ' % (self.name, upstream))
2452
Pierre Tardy3d125942012-05-04 12:18:12 +02002453 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002454 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002455 if ffonly:
2456 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002457 if GitCommand(self, cmd).Wait() != 0:
2458 raise GitError('%s merge %s ' % (self.name, head))
2459
David Pursehousee8ace262020-02-13 12:41:15 +09002460 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002461 init_git_dir = not os.path.exists(self.gitdir)
2462 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002463 try:
2464 # Initialize the bare repository, which contains all of the objects.
2465 if init_obj_dir:
2466 os.makedirs(self.objdir)
2467 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002468
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002469 if self.use_git_worktrees:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002470 # Enable per-worktree config file support if possible. This is more a
2471 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002472 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002473 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002474
Kevin Degib1a07b82015-07-27 13:33:43 -06002475 # If we have a separate directory to hold refs, initialize it as well.
2476 if self.objdir != self.gitdir:
2477 if init_git_dir:
2478 os.makedirs(self.gitdir)
2479
2480 if init_obj_dir or init_git_dir:
2481 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2482 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002483 try:
2484 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2485 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002486 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002487 print("Retrying clone after deleting %s" %
2488 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002489 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002490 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2491 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002492 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002493 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002494 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2495 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002496 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002497 raise e
2498 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002499
Kevin Degi384b3c52014-10-16 16:02:58 -06002500 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002501 mp = self.manifest.manifestProject
2502 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002503
Kevin Degib1a07b82015-07-27 13:33:43 -06002504 if ref_dir or mirror_git:
2505 if not mirror_git:
2506 mirror_git = os.path.join(ref_dir, self.name + '.git')
2507 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2508 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002509 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2510 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002511
Kevin Degib1a07b82015-07-27 13:33:43 -06002512 if os.path.exists(mirror_git):
2513 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002514 elif os.path.exists(repo_git):
2515 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002516 elif os.path.exists(worktrees_git):
2517 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002518 else:
2519 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002520
Kevin Degib1a07b82015-07-27 13:33:43 -06002521 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002522 if not os.path.isabs(ref_dir):
2523 # The alternate directory is relative to the object database.
2524 ref_dir = os.path.relpath(ref_dir,
2525 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002526 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2527 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002528
David Pursehousee8ace262020-02-13 12:41:15 +09002529 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002530
2531 m = self.manifest.manifestProject.config
2532 for key in ['user.name', 'user.email']:
2533 if m.Has(key, include_defaults=False):
2534 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002535 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002536 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002537 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002538 except Exception:
2539 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002540 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002541 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002542 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002543 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002544
David Pursehousee8ace262020-02-13 12:41:15 +09002545 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002546 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002547 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002548
David Pursehousee8ace262020-02-13 12:41:15 +09002549 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002550 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002551 if not os.path.exists(hooks):
2552 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002553 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002554 name = os.path.basename(stock_hook)
2555
Victor Boivie65e0f352011-04-18 11:23:29 +02002556 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002557 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002558 # Don't install a Gerrit Code Review hook if this
2559 # project does not appear to use it for reviews.
2560 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002561 # Since the manifest project is one of those, but also
2562 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002563 continue
2564
2565 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002566 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002567 continue
2568 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002569 # If the files are the same, we'll leave it alone. We create symlinks
2570 # below by default but fallback to hardlinks if the OS blocks them.
2571 # So if we're here, it's probably because we made a hardlink below.
2572 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002573 if not quiet:
2574 _warn("%s: Not replacing locally modified %s hook",
2575 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002576 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002577 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002578 platform_utils.symlink(
2579 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002580 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002581 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002582 try:
2583 os.link(stock_hook, dst)
2584 except OSError:
2585 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002586 else:
2587 raise
2588
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002589 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002590 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002591 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002592 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002593 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002594 remote.review = self.remote.review
2595 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002596
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002597 if self.worktree:
2598 remote.ResetFetch(mirror=False)
2599 else:
2600 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002601 remote.Save()
2602
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002603 def _InitMRef(self):
2604 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002605 if self.use_git_worktrees:
Mike Frysinger29626b42021-05-01 09:37:13 -04002606 # Set up the m/ space to point to the worktree-specific ref space.
2607 # We'll update the worktree-specific ref space on each checkout.
2608 ref = R_M + self.manifest.branch
2609 if not self.bare_ref.symref(ref):
2610 self.bare_git.symbolic_ref(
2611 '-m', 'redirecting to worktree scope',
2612 ref, R_WORKTREE_M + self.manifest.branch)
2613
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002614 # We can't update this ref with git worktrees until it exists.
2615 # We'll wait until the initial checkout to set it.
2616 if not os.path.exists(self.worktree):
2617 return
2618
2619 base = R_WORKTREE_M
2620 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002621
2622 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002623 else:
2624 base = R_M
2625 active_git = self.bare_git
2626
2627 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002628
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002629 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002630 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002631
Remy Böhmer1469c282020-12-15 18:49:02 +01002632 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002633 cur = self.bare_ref.symref(ref)
2634
2635 if self.revisionId:
2636 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2637 msg = 'manifest set to %s' % self.revisionId
2638 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002639 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002640 else:
2641 remote = self.GetRemote(self.remote.name)
2642 dst = remote.ToLocal(self.revisionExpr)
2643 if cur != dst:
2644 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002645 if detach:
2646 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2647 else:
2648 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002649
Kevin Degi384b3c52014-10-16 16:02:58 -06002650 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002651 # Git worktrees don't use symlinks to share at all.
2652 if self.use_git_worktrees:
2653 return
2654
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002655 symlink_files = self.shareable_files[:]
2656 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002657 if share_refs:
2658 symlink_files += self.working_tree_files
2659 symlink_dirs += self.working_tree_dirs
2660 to_symlink = symlink_files + symlink_dirs
2661 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002662 # Try to self-heal a bit in simple cases.
2663 dst_path = os.path.join(destdir, name)
2664 src_path = os.path.join(srcdir, name)
2665
2666 if name in self.working_tree_dirs:
2667 # If the dir is missing under .repo/projects/, create it.
2668 if not os.path.exists(src_path):
2669 os.makedirs(src_path)
2670
2671 elif name in self.working_tree_files:
2672 # If it's a file under the checkout .git/ and the .repo/projects/ has
2673 # nothing, move the file under the .repo/projects/ tree.
2674 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2675 platform_utils.rename(dst_path, src_path)
2676
2677 # If the path exists under the .repo/projects/ and there's no symlink
2678 # under the checkout .git/, recreate the symlink.
2679 if name in self.working_tree_dirs or name in self.working_tree_files:
2680 if os.path.exists(src_path) and not os.path.exists(dst_path):
2681 platform_utils.symlink(
2682 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2683
2684 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002685 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002686 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002687 # Fail if the links are pointing to the wrong place
2688 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002689 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002690 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002691 'work tree. If you\'re comfortable with the '
2692 'possibility of losing the work tree\'s git metadata,'
2693 ' use `repo sync --force-sync {0}` to '
2694 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002695
David James8d201162013-10-11 17:03:19 -07002696 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2697 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2698
2699 Args:
2700 gitdir: The bare git repository. Must already be initialized.
2701 dotgit: The repository you would like to initialize.
2702 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2703 Only one work tree can store refs under a given |gitdir|.
2704 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2705 This saves you the effort of initializing |dotgit| yourself.
2706 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002707 symlink_files = self.shareable_files[:]
2708 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002709 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002710 symlink_files += self.working_tree_files
2711 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002712 to_symlink = symlink_files + symlink_dirs
2713
2714 to_copy = []
2715 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002716 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002717
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002718 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002719 for name in set(to_copy).union(to_symlink):
2720 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002721 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002722 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002723
Kevin Degi384b3c52014-10-16 16:02:58 -06002724 if os.path.lexists(dst):
2725 continue
David James8d201162013-10-11 17:03:19 -07002726
2727 # If the source dir doesn't exist, create an empty dir.
2728 if name in symlink_dirs and not os.path.lexists(src):
2729 os.makedirs(src)
2730
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002731 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002732 platform_utils.symlink(
2733 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002734 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002735 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002736 shutil.copytree(src, dst)
2737 elif os.path.isfile(src):
2738 shutil.copy(src, dst)
2739
Conley Owens80b87fe2014-05-09 17:13:44 -07002740 # If the source file doesn't exist, ensure the destination
2741 # file doesn't either.
2742 if name in symlink_files and not os.path.lexists(src):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002743 platform_utils.remove(dst, missing_ok=True)
Conley Owens80b87fe2014-05-09 17:13:44 -07002744
David James8d201162013-10-11 17:03:19 -07002745 except OSError as e:
2746 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002747 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002748 else:
2749 raise
2750
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002751 def _InitGitWorktree(self):
2752 """Init the project using git worktrees."""
2753 self.bare_git.worktree('prune')
2754 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2755 self.worktree, self.GetRevisionId())
2756
2757 # Rewrite the internal state files to use relative paths between the
2758 # checkouts & worktrees.
2759 dotgit = os.path.join(self.worktree, '.git')
2760 with open(dotgit, 'r') as fp:
2761 # Figure out the checkout->worktree path.
2762 setting = fp.read()
2763 assert setting.startswith('gitdir:')
2764 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002765 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2766 # of file permissions. Delete it and recreate it from scratch to avoid.
2767 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002768 # Use relative path from checkout->worktree & maintain Unix line endings
2769 # on all OS's to match git behavior.
2770 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002771 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2772 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002773 # Use relative path from worktree->checkout & maintain Unix line endings
2774 # on all OS's to match git behavior.
2775 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002776 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2777
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002778 self._InitMRef()
2779
Martin Kellye4e94d22017-03-21 16:05:12 -07002780 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002781 realdotgit = os.path.join(self.worktree, '.git')
2782 tmpdotgit = realdotgit + '.tmp'
2783 init_dotgit = not os.path.exists(realdotgit)
2784 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002785 if self.use_git_worktrees:
2786 self._InitGitWorktree()
2787 self._CopyAndLinkFiles()
2788 return
2789
Mike Frysingerf4545122019-11-11 04:34:16 -05002790 dotgit = tmpdotgit
2791 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2792 os.makedirs(tmpdotgit)
2793 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2794 copy_all=False)
2795 else:
2796 dotgit = realdotgit
2797
Kevin Degib1a07b82015-07-27 13:33:43 -06002798 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002799 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2800 except GitError as e:
2801 if force_sync and not init_dotgit:
2802 try:
2803 platform_utils.rmtree(dotgit)
2804 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002805 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002806 raise e
2807 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808
Mike Frysingerf4545122019-11-11 04:34:16 -05002809 if init_dotgit:
2810 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002811
Mike Frysingerf4545122019-11-11 04:34:16 -05002812 # Now that the .git dir is fully set up, move it to its final home.
2813 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814
Mike Frysingerf4545122019-11-11 04:34:16 -05002815 # Finish checking out the worktree.
2816 cmd = ['read-tree', '--reset', '-u']
2817 cmd.append('-v')
2818 cmd.append(HEAD)
2819 if GitCommand(self, cmd).Wait() != 0:
2820 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002821
Mike Frysingerf4545122019-11-11 04:34:16 -05002822 if submodules:
2823 self._SyncSubmodules(quiet=True)
2824 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002825
Renaud Paquay788e9622017-01-27 11:41:12 -08002826 def _get_symlink_error_message(self):
2827 if platform_utils.isWindows():
2828 return ('Unable to create symbolic link. Please re-run the command as '
2829 'Administrator, or see '
2830 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2831 'for other options.')
2832 return 'filesystem must support symlinks'
2833
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002835 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002837 def _revlist(self, *args, **kw):
2838 a = []
2839 a.extend(args)
2840 a.append('--')
2841 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002842
2843 @property
2844 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002845 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002846
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002847 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002848 """Get logs between two revisions of this project."""
2849 comp = '..'
2850 if rev1:
2851 revs = [rev1]
2852 if rev2:
2853 revs.extend([comp, rev2])
2854 cmd = ['log', ''.join(revs)]
2855 out = DiffColoring(self.config)
2856 if out.is_on and color:
2857 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002858 if pretty_format is not None:
2859 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002860 if oneline:
2861 cmd.append('--oneline')
2862
2863 try:
2864 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2865 if log.Wait() == 0:
2866 return log.stdout
2867 except GitError:
2868 # worktree may not exist if groups changed for example. In that case,
2869 # try in gitdir instead.
2870 if not os.path.exists(self.worktree):
2871 return self.bare_git.log(*cmd[1:])
2872 else:
2873 raise
2874 return None
2875
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002876 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2877 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002878 """Get the list of logs from this revision to given revisionId"""
2879 logs = {}
2880 selfId = self.GetRevisionId(self._allrefs)
2881 toId = toProject.GetRevisionId(toProject._allrefs)
2882
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002883 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2884 pretty_format=pretty_format)
2885 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2886 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002887 return logs
2888
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002889 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002890
David James8d201162013-10-11 17:03:19 -07002891 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892 self._project = project
2893 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002894 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002895
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002896 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2897 def __getstate__(self):
2898 return (self._project, self._bare, self._gitdir)
2899
2900 def __setstate__(self, state):
2901 self._project, self._bare, self._gitdir = state
2902
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002903 def LsOthers(self):
2904 p = GitCommand(self._project,
2905 ['ls-files',
2906 '-z',
2907 '--others',
2908 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002909 bare=False,
David James8d201162013-10-11 17:03:19 -07002910 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002911 capture_stdout=True,
2912 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002913 if p.Wait() == 0:
2914 out = p.stdout
2915 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002916 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002917 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002918 return []
2919
2920 def DiffZ(self, name, *args):
2921 cmd = [name]
2922 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002923 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002924 cmd.extend(args)
2925 p = GitCommand(self._project,
2926 cmd,
David James8d201162013-10-11 17:03:19 -07002927 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002928 bare=False,
2929 capture_stdout=True,
2930 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002931 p.Wait()
2932 r = {}
2933 out = p.stdout
2934 if out:
2935 out = iter(out[:-1].split('\0'))
2936 while out:
2937 try:
2938 info = next(out)
2939 path = next(out)
2940 except StopIteration:
2941 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002942
Mike Frysinger84230002021-02-16 17:08:35 -05002943 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002944
Mike Frysinger84230002021-02-16 17:08:35 -05002945 def __init__(self, path, omode, nmode, oid, nid, state):
2946 self.path = path
2947 self.src_path = None
2948 self.old_mode = omode
2949 self.new_mode = nmode
2950 self.old_id = oid
2951 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002952
Mike Frysinger84230002021-02-16 17:08:35 -05002953 if len(state) == 1:
2954 self.status = state
2955 self.level = None
2956 else:
2957 self.status = state[:1]
2958 self.level = state[1:]
2959 while self.level.startswith('0'):
2960 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002961
Mike Frysinger84230002021-02-16 17:08:35 -05002962 info = info[1:].split(' ')
2963 info = _Info(path, *info)
2964 if info.status in ('R', 'C'):
2965 info.src_path = info.path
2966 info.path = next(out)
2967 r[info.path] = info
2968 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002969
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002970 def GetDotgitPath(self, subpath=None):
2971 """Return the full path to the .git dir.
2972
2973 As a convenience, append |subpath| if provided.
2974 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002975 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002976 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002977 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002978 dotgit = os.path.join(self._project.worktree, '.git')
2979 if os.path.isfile(dotgit):
2980 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2981 with open(dotgit) as fp:
2982 setting = fp.read()
2983 assert setting.startswith('gitdir:')
2984 gitdir = setting.split(':', 1)[1].strip()
2985 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2986
2987 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2988
2989 def GetHead(self):
2990 """Return the ref that HEAD points to."""
2991 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002992 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002993 with open(path) as fd:
2994 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002995 except IOError as e:
2996 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002997 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302998 line = line.decode()
2999 except AttributeError:
3000 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003001 if line.startswith('ref: '):
3002 return line[5:-1]
3003 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003004
3005 def SetHead(self, ref, message=None):
3006 cmdv = []
3007 if message is not None:
3008 cmdv.extend(['-m', message])
3009 cmdv.append(HEAD)
3010 cmdv.append(ref)
3011 self.symbolic_ref(*cmdv)
3012
3013 def DetachHead(self, new, message=None):
3014 cmdv = ['--no-deref']
3015 if message is not None:
3016 cmdv.extend(['-m', message])
3017 cmdv.append(HEAD)
3018 cmdv.append(new)
3019 self.update_ref(*cmdv)
3020
3021 def UpdateRef(self, name, new, old=None,
3022 message=None,
3023 detach=False):
3024 cmdv = []
3025 if message is not None:
3026 cmdv.extend(['-m', message])
3027 if detach:
3028 cmdv.append('--no-deref')
3029 cmdv.append(name)
3030 cmdv.append(new)
3031 if old is not None:
3032 cmdv.append(old)
3033 self.update_ref(*cmdv)
3034
3035 def DeleteRef(self, name, old=None):
3036 if not old:
3037 old = self.rev_parse(name)
3038 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003039 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003040
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003041 def rev_list(self, *args, **kw):
3042 if 'format' in kw:
3043 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3044 else:
3045 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003046 cmdv.extend(args)
3047 p = GitCommand(self._project,
3048 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003049 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003050 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003051 capture_stdout=True,
3052 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003053 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003054 raise GitError('%s rev-list %s: %s' %
3055 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003056 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003057
3058 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003059 """Allow arbitrary git commands using pythonic syntax.
3060
3061 This allows you to do things like:
3062 git_obj.rev_parse('HEAD')
3063
3064 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3065 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003066 Any other positional arguments will be passed to the git command, and the
3067 following keyword arguments are supported:
3068 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003069
3070 Args:
3071 name: The name of the git command to call. Any '_' characters will
3072 be replaced with '-'.
3073
3074 Returns:
3075 A callable object that will try to call git with the named command.
3076 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003077 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003078
Dave Borowitz091f8932012-10-23 17:01:04 -07003079 def runner(*args, **kwargs):
3080 cmdv = []
3081 config = kwargs.pop('config', None)
3082 for k in kwargs:
3083 raise TypeError('%s() got an unexpected keyword argument %r'
3084 % (name, k))
3085 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303086 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003087 cmdv.append('-c')
3088 cmdv.append('%s=%s' % (k, v))
3089 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003090 cmdv.extend(args)
3091 p = GitCommand(self._project,
3092 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003093 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003094 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003095 capture_stdout=True,
3096 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003097 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003098 raise GitError('%s %s: %s' %
3099 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003100 r = p.stdout
3101 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3102 return r[:-1]
3103 return r
3104 return runner
3105
3106
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003107class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003108
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003109 def __str__(self):
3110 return 'prior sync failed; rebase still in progress'
3111
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003112
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003113class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003114
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003115 def __str__(self):
3116 return 'contains uncommitted changes'
3117
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003118
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003119class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003120
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003121 def __init__(self, project, text):
3122 self.project = project
3123 self.text = text
3124
3125 def Print(self, syncbuf):
3126 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3127 syncbuf.out.nl()
3128
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003129
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003130class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003131
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003132 def __init__(self, project, why):
3133 self.project = project
3134 self.why = why
3135
3136 def Print(self, syncbuf):
3137 syncbuf.out.fail('error: %s/: %s',
3138 self.project.relpath,
3139 str(self.why))
3140 syncbuf.out.nl()
3141
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003142
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003143class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003144
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003145 def __init__(self, project, action):
3146 self.project = project
3147 self.action = action
3148
3149 def Run(self, syncbuf):
3150 out = syncbuf.out
3151 out.project('project %s/', self.project.relpath)
3152 out.nl()
3153 try:
3154 self.action()
3155 out.nl()
3156 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003157 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003158 out.nl()
3159 return False
3160
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003161
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003162class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003163
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003164 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003165 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003166 self.project = self.printer('header', attr='bold')
3167 self.info = self.printer('info')
3168 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003169
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003170
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003171class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003172
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003173 def __init__(self, config, detach_head=False):
3174 self._messages = []
3175 self._failures = []
3176 self._later_queue1 = []
3177 self._later_queue2 = []
3178
3179 self.out = _SyncColoring(config)
3180 self.out.redirect(sys.stderr)
3181
3182 self.detach_head = detach_head
3183 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003184 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003185
3186 def info(self, project, fmt, *args):
3187 self._messages.append(_InfoMessage(project, fmt % args))
3188
3189 def fail(self, project, err=None):
3190 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003191 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003192
3193 def later1(self, project, what):
3194 self._later_queue1.append(_Later(project, what))
3195
3196 def later2(self, project, what):
3197 self._later_queue2.append(_Later(project, what))
3198
3199 def Finish(self):
3200 self._PrintMessages()
3201 self._RunLater()
3202 self._PrintMessages()
3203 return self.clean
3204
David Rileye0684ad2017-04-05 00:02:59 -07003205 def Recently(self):
3206 recent_clean = self.recent_clean
3207 self.recent_clean = True
3208 return recent_clean
3209
3210 def _MarkUnclean(self):
3211 self.clean = False
3212 self.recent_clean = False
3213
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003214 def _RunLater(self):
3215 for q in ['_later_queue1', '_later_queue2']:
3216 if not self._RunQueue(q):
3217 return
3218
3219 def _RunQueue(self, queue):
3220 for m in getattr(self, queue):
3221 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003222 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003223 return False
3224 setattr(self, queue, [])
3225 return True
3226
3227 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003228 if self._messages or self._failures:
3229 if os.isatty(2):
3230 self.out.write(progress.CSI_ERASE_LINE)
3231 self.out.write('\r')
3232
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003233 for m in self._messages:
3234 m.Print(self)
3235 for m in self._failures:
3236 m.Print(self)
3237
3238 self._messages = []
3239 self._failures = []
3240
3241
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003242class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003243
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003244 """A special project housed under .repo.
3245 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003246
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003247 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003248 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003249 manifest=manifest,
3250 name=name,
3251 gitdir=gitdir,
3252 objdir=gitdir,
3253 worktree=worktree,
3254 remote=RemoteSpec('origin'),
3255 relpath='.repo/%s' % name,
3256 revisionExpr='refs/heads/master',
3257 revisionId=None,
3258 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003259
3260 def PreSync(self):
3261 if self.Exists:
3262 cb = self.CurrentBranch
3263 if cb:
3264 base = self.GetBranch(cb).merge
3265 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003266 self.revisionExpr = base
3267 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003268
Martin Kelly224a31a2017-07-10 14:46:25 -07003269 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003270 """ Prepare MetaProject for manifest branch switch
3271 """
3272
3273 # detach and delete manifest branch, allowing a new
3274 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003275 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003276 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003277 syncbuf.Finish()
3278
3279 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003280 ['update-ref', '-d', 'refs/heads/default'],
3281 capture_stdout=True,
3282 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003283
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003284 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003285 def LastFetch(self):
3286 try:
3287 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3288 return os.path.getmtime(fh)
3289 except OSError:
3290 return 0
3291
3292 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003293 def HasChanges(self):
3294 """Has the remote received new commits not yet checked out?
3295 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003296 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003297 return False
3298
David Pursehouse8a68ff92012-09-24 12:15:13 +09003299 all_refs = self.bare_ref.all
3300 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003301 head = self.work_git.GetHead()
3302 if head.startswith(R_HEADS):
3303 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003304 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003305 except KeyError:
3306 head = None
3307
3308 if revid == head:
3309 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003310 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003311 return True
3312 return False