blob: a9deda42609f24738e69f5da95c51fca10ddd17f [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):
235 Coloring.__init__(self, 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):
249 Coloring.__init__(self, 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
Anthony King7bdac712014-07-16 12:56:40 +0100254class _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
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700261
Mike Frysingere6a202f2019-08-02 15:57:57 -0400262def _SafeExpandPath(base, subpath, skipfinal=False):
263 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Mike Frysingere6a202f2019-08-02 15:57:57 -0400265 We make sure no intermediate symlinks are traversed, and that the final path
266 is not a special file (e.g. not a socket or fifo).
267
268 NB: We rely on a number of paths already being filtered out while parsing the
269 manifest. See the validation logic in manifest_xml.py for more details.
270 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500271 # Split up the path by its components. We can't use os.path.sep exclusively
272 # as some platforms (like Windows) will convert / to \ and that bypasses all
273 # our constructed logic here. Especially since manifest authors only use
274 # / in their paths.
275 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
276 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400277 if skipfinal:
278 # Whether the caller handles the final component itself.
279 finalpart = components.pop()
280
281 path = base
282 for part in components:
283 if part in {'.', '..'}:
284 raise ManifestInvalidPathError(
285 '%s: "%s" not allowed in paths' % (subpath, part))
286
287 path = os.path.join(path, part)
288 if platform_utils.islink(path):
289 raise ManifestInvalidPathError(
290 '%s: traversing symlinks not allow' % (path,))
291
292 if os.path.exists(path):
293 if not os.path.isfile(path) and not platform_utils.isdir(path):
294 raise ManifestInvalidPathError(
295 '%s: only regular files & directories allowed' % (path,))
296
297 if skipfinal:
298 path = os.path.join(path, finalpart)
299
300 return path
301
302
303class _CopyFile(object):
304 """Container for <copyfile> manifest element."""
305
306 def __init__(self, git_worktree, src, topdir, dest):
307 """Register a <copyfile> request.
308
309 Args:
310 git_worktree: Absolute path to the git project checkout.
311 src: Relative path under |git_worktree| of file to read.
312 topdir: Absolute path to the top of the repo client checkout.
313 dest: Relative path under |topdir| of file to write.
314 """
315 self.git_worktree = git_worktree
316 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700317 self.src = src
318 self.dest = dest
319
320 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400321 src = _SafeExpandPath(self.git_worktree, self.src)
322 dest = _SafeExpandPath(self.topdir, self.dest)
323
324 if platform_utils.isdir(src):
325 raise ManifestInvalidPathError(
326 '%s: copying from directory not supported' % (self.src,))
327 if platform_utils.isdir(dest):
328 raise ManifestInvalidPathError(
329 '%s: copying to directory not allowed' % (self.dest,))
330
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700331 # copy file if it does not exist or is out of date
332 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
333 try:
334 # remove existing file first, since it might be read-only
335 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800336 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400337 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200338 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700339 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200340 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341 shutil.copy(src, dest)
342 # make the file read-only
343 mode = os.stat(dest)[stat.ST_MODE]
344 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
345 os.chmod(dest, mode)
346 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700347 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Anthony King7bdac712014-07-16 12:56:40 +0100350class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400351 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700352
Mike Frysingere6a202f2019-08-02 15:57:57 -0400353 def __init__(self, git_worktree, src, topdir, dest):
354 """Register a <linkfile> request.
355
356 Args:
357 git_worktree: Absolute path to the git project checkout.
358 src: Target of symlink relative to path under |git_worktree|.
359 topdir: Absolute path to the top of the repo client checkout.
360 dest: Relative path under |topdir| of symlink to create.
361 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700362 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400363 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500364 self.src = src
365 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500366
Wink Saville4c426ef2015-06-03 08:05:17 -0700367 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500368 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700369 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500370 try:
371 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800372 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800373 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500374 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700375 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700376 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500377 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700378 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500379 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700380 _error('Cannot link file %s to %s', relSrc, absDest)
381
382 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400383 """Link the self.src & self.dest paths.
384
385 Handles wild cards on the src linking all of the files in the source in to
386 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700387 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500388 # Some people use src="." to create stable links to projects. Lets allow
389 # that but reject all other uses of "." to keep things simple.
390 if self.src == '.':
391 src = self.git_worktree
392 else:
393 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400394
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300395 if not glob.has_magic(src):
396 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400397 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
398 # dest & src are absolute paths at this point. Make sure the target of
399 # the symlink is relative in the context of the repo client checkout.
400 relpath = os.path.relpath(src, os.path.dirname(dest))
401 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700402 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400403 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300404 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400405 if os.path.exists(dest) and not platform_utils.isdir(dest):
406 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700407 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400408 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700409 # Create a releative path from source dir to destination dir
410 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400411 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700412
413 # Get the source file name
414 srcFile = os.path.basename(absSrcFile)
415
416 # Now form the final full paths to srcFile. They will be
417 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400418 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700419 relSrc = os.path.join(relSrcDir, srcFile)
420 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500421
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700422
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700423class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700424
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700425 def __init__(self,
426 name,
Anthony King7bdac712014-07-16 12:56:40 +0100427 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700428 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100429 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700430 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700431 orig_name=None,
432 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700433 self.name = name
434 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700435 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700436 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100437 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700438 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700439 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700440
Ian Kasprzak0286e312021-02-05 10:06:18 -0800441
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700442class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600443 # These objects can be shared between several working trees.
444 shareable_files = ['description', 'info']
445 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
446 # These objects can only be used by a single working tree.
447 working_tree_files = ['config', 'packed-refs', 'shallow']
448 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 def __init__(self,
451 manifest,
452 name,
453 remote,
454 gitdir,
David James8d201162013-10-11 17:03:19 -0700455 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456 worktree,
457 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700458 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800459 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100460 rebase=True,
461 groups=None,
462 sync_c=False,
463 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900464 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100465 clone_depth=None,
466 upstream=None,
467 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500468 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100469 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900470 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700471 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600472 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700473 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800474 """Init a Project object.
475
476 Args:
477 manifest: The XmlManifest object.
478 name: The `name` attribute of manifest.xml's project element.
479 remote: RemoteSpec object specifying its remote's properties.
480 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700481 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800482 worktree: Absolute path of git working tree.
483 relpath: Relative path of git working tree to repo's top directory.
484 revisionExpr: The `revision` attribute of manifest.xml's project element.
485 revisionId: git commit id for checking out.
486 rebase: The `rebase` attribute of manifest.xml's project element.
487 groups: The `groups` attribute of manifest.xml's project element.
488 sync_c: The `sync-c` attribute of manifest.xml's project element.
489 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900490 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800491 upstream: The `upstream` attribute of manifest.xml's project element.
492 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500493 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800494 is_derived: False if the project was explicitly defined in the manifest;
495 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400496 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900497 optimized_fetch: If True, when a project is set to a sha1 revision, only
498 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600499 retry_fetches: Retry remote fetches n times upon receiving transient error
500 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700501 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800502 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400503 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700504 self.name = name
505 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800506 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700507 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800508 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700509 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800510 else:
511 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700512 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700513 self.revisionExpr = revisionExpr
514
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700515 if revisionId is None \
516 and revisionExpr \
517 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700518 self.revisionId = revisionExpr
519 else:
520 self.revisionId = revisionId
521
Mike Pontillod3153822012-02-28 11:53:24 -0800522 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700523 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700524 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800525 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900526 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900527 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700528 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800529 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500530 # NB: Do not use this setting in __init__ to change behavior so that the
531 # manifest.git checkout can inspect & change it after instantiating. See
532 # the XmlManifest init code for more info.
533 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800534 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900535 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600536 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800537 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800538
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700539 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700540 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500541 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500542 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700543 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400544 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700545
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800546 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700547 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800548 else:
549 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700550 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700551 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700552 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400553 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700554 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555
Doug Anderson37282b42011-03-04 11:54:18 -0800556 # This will be filled in if a project is later identified to be the
557 # project containing repo hooks.
558 self.enabled_repo_hooks = []
559
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700560 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800561 def Derived(self):
562 return self.is_derived
563
564 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700566 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700567
568 @property
569 def CurrentBranch(self):
570 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400571
572 The branch name omits the 'refs/heads/' prefix.
573 None is returned if the project is on a detached HEAD, or if the work_git is
574 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400576 try:
577 b = self.work_git.GetHead()
578 except NoManifestException:
579 # If the local checkout is in a bad state, don't barf. Let the callers
580 # process this like the head is unreadable.
581 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 if b.startswith(R_HEADS):
583 return b[len(R_HEADS):]
584 return None
585
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700586 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500587 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
588 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
589 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200590
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591 def IsDirty(self, consider_untracked=True):
592 """Is the working directory modified in some way?
593 """
594 self.work_git.update_index('-q',
595 '--unmerged',
596 '--ignore-missing',
597 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900598 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599 return True
600 if self.work_git.DiffZ('diff-files'):
601 return True
602 if consider_untracked and self.work_git.LsOthers():
603 return True
604 return False
605
606 _userident_name = None
607 _userident_email = None
608
609 @property
610 def UserName(self):
611 """Obtain the user's personal name.
612 """
613 if self._userident_name is None:
614 self._LoadUserIdentity()
615 return self._userident_name
616
617 @property
618 def UserEmail(self):
619 """Obtain the user's email address. This is very likely
620 to be their Gerrit login.
621 """
622 if self._userident_email is None:
623 self._LoadUserIdentity()
624 return self._userident_email
625
626 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900627 u = self.bare_git.var('GIT_COMMITTER_IDENT')
628 m = re.compile("^(.*) <([^>]*)> ").match(u)
629 if m:
630 self._userident_name = m.group(1)
631 self._userident_email = m.group(2)
632 else:
633 self._userident_name = ''
634 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700635
636 def GetRemote(self, name):
637 """Get the configuration for a single remote.
638 """
639 return self.config.GetRemote(name)
640
641 def GetBranch(self, name):
642 """Get the configuration for a single branch.
643 """
644 return self.config.GetBranch(name)
645
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700646 def GetBranches(self):
647 """Get all existing local branches.
648 """
649 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900650 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700651 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700652
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530653 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700654 if name.startswith(R_HEADS):
655 name = name[len(R_HEADS):]
656 b = self.GetBranch(name)
657 b.current = name == current
658 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900659 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700660 heads[name] = b
661
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530662 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700663 if name.startswith(R_PUB):
664 name = name[len(R_PUB):]
665 b = heads.get(name)
666 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900667 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700668
669 return heads
670
Colin Cross5acde752012-03-28 20:15:45 -0700671 def MatchesGroups(self, manifest_groups):
672 """Returns true if the manifest groups specified at init should cause
673 this project to be synced.
674 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700675 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700676
677 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700678 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700679 manifest_groups: "-group1,group2"
680 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500681
682 The special manifest group "default" will match any project that
683 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700684 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500685 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700686 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700687 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500688 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700689
Conley Owens971de8e2012-04-16 10:36:08 -0700690 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700691 for group in expanded_manifest_groups:
692 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700693 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700694 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700695 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700696
Conley Owens971de8e2012-04-16 10:36:08 -0700697 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700699# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700700 def UncommitedFiles(self, get_all=True):
701 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700703 Args:
704 get_all: a boolean, if True - get information about all different
705 uncommitted files. If False - return as soon as any kind of
706 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500707 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700708 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500709 self.work_git.update_index('-q',
710 '--unmerged',
711 '--ignore-missing',
712 '--refresh')
713 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700714 details.append("rebase in progress")
715 if not get_all:
716 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500717
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700718 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
719 if changes:
720 details.extend(changes)
721 if not get_all:
722 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500723
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 changes = self.work_git.DiffZ('diff-files').keys()
725 if changes:
726 details.extend(changes)
727 if not get_all:
728 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500729
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700730 changes = self.work_git.LsOthers()
731 if changes:
732 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500733
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 return details
735
736 def HasChanges(self):
737 """Returns true if there are uncommitted changes.
738 """
739 if self.UncommitedFiles(get_all=False):
740 return True
741 else:
742 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500743
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600744 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200746
747 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200748 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600749 quiet: If True then only print the project name. Do not print
750 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700752 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700753 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200754 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700755 print(file=output_redir)
756 print('project %s/' % self.relpath, file=output_redir)
757 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 return
759
760 self.work_git.update_index('-q',
761 '--unmerged',
762 '--ignore-missing',
763 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700764 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
766 df = self.work_git.DiffZ('diff-files')
767 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100768 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700769 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700770
771 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700772 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200773 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700774 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600776 if quiet:
777 out.nl()
778 return 'DIRTY'
779
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 branch = self.CurrentBranch
781 if branch is None:
782 out.nobranch('(*** NO BRANCH ***)')
783 else:
784 out.branch('branch %s', branch)
785 out.nl()
786
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700787 if rb:
788 out.important('prior sync failed; rebase still in progress')
789 out.nl()
790
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791 paths = list()
792 paths.extend(di.keys())
793 paths.extend(df.keys())
794 paths.extend(do)
795
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530796 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900797 try:
798 i = di[p]
799 except KeyError:
800 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900802 try:
803 f = df[p]
804 except KeyError:
805 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200806
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900807 if i:
808 i_status = i.status.upper()
809 else:
810 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900812 if f:
813 f_status = f.status.lower()
814 else:
815 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700816
817 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800818 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700819 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820 else:
821 line = ' %s%s\t%s' % (i_status, f_status, p)
822
823 if i and not f:
824 out.added('%s', line)
825 elif (i and f) or (not i and f):
826 out.changed('%s', line)
827 elif not i and not f:
828 out.untracked('%s', line)
829 else:
830 out.write('%s', line)
831 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200832
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700833 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
pelyad67872d2012-03-28 14:49:58 +0300835 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 """Prints the status of the repository to stdout.
837 """
838 out = DiffColoring(self.config)
839 cmd = ['diff']
840 if out.is_on:
841 cmd.append('--color')
842 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300843 if absolute_paths:
844 cmd.append('--src-prefix=a/%s/' % self.relpath)
845 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700846 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400847 try:
848 p = GitCommand(self,
849 cmd,
850 capture_stdout=True,
851 capture_stderr=True)
852 except GitError as e:
853 out.nl()
854 out.project('project %s/' % self.relpath)
855 out.nl()
856 out.fail('%s', str(e))
857 out.nl()
858 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700859 has_diff = False
860 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -0400861 if not hasattr(line, 'encode'):
862 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863 if not has_diff:
864 out.nl()
865 out.project('project %s/' % self.relpath)
866 out.nl()
867 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -0700868 print(line[:-1])
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400869 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700871# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900872 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 """Was the branch published (uploaded) for code review?
874 If so, returns the SHA-1 hash of the last published
875 state for the branch.
876 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700877 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900878 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700879 try:
880 return self.bare_git.rev_parse(key)
881 except GitError:
882 return None
883 else:
884 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900885 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700886 except KeyError:
887 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888
David Pursehouse8a68ff92012-09-24 12:15:13 +0900889 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700890 """Prunes any stale published refs.
891 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900892 if all_refs is None:
893 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700894 heads = set()
895 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530896 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700897 if name.startswith(R_HEADS):
898 heads.add(name)
899 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900900 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530902 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700903 n = name[len(R_PUB):]
904 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900905 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700907 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908 """List any branches which can be uploaded for review.
909 """
910 heads = {}
911 pubed = {}
912
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530913 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900915 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900917 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700918
919 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530920 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900921 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700923 if selected_branch and branch != selected_branch:
924 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700925
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800926 rb = self.GetUploadableBranch(branch)
927 if rb:
928 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929 return ready
930
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800931 def GetUploadableBranch(self, branch_name):
932 """Get a single uploadable branch, or None.
933 """
934 branch = self.GetBranch(branch_name)
935 base = branch.LocalMerge
936 if branch.LocalMerge:
937 rb = ReviewableBranch(self, branch, base)
938 if rb.commits:
939 return rb
940 return None
941
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700942 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100943 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500944 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700945 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500946 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500947 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200948 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700949 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200950 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200951 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800952 validate_certs=True,
953 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954 """Uploads the named branch for code review.
955 """
956 if branch is None:
957 branch = self.CurrentBranch
958 if branch is None:
959 raise GitError('not currently on a branch')
960
961 branch = self.GetBranch(branch)
962 if not branch.LocalMerge:
963 raise GitError('branch %s does not track a remote' % branch.name)
964 if not branch.remote.review:
965 raise GitError('remote %s has no review url' % branch.remote.name)
966
Bryan Jacobsf609f912013-05-06 13:36:24 -0400967 if dest_branch is None:
968 dest_branch = self.dest_branch
969 if dest_branch is None:
970 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700971 if not dest_branch.startswith(R_HEADS):
972 dest_branch = R_HEADS + dest_branch
973
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800974 if not branch.remote.projectname:
975 branch.remote.projectname = self.name
976 branch.remote.Save()
977
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200978 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800979 if url is None:
980 raise UploadError('review not configured')
981 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500982 if dryrun:
983 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800984
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800985 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -0800986 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700987
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800988 for push_option in (push_options or []):
989 cmd.append('-o')
990 cmd.append(push_option)
991
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800992 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800993
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800994 if dest_branch.startswith(R_HEADS):
995 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -0700996
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -0500997 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -0800998 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800999 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001000 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001001 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001002 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001003
David Pursehousef25a3702018-11-14 19:01:22 -08001004 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001005 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001006 if notify:
1007 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001008 if private:
1009 opts += ['private']
1010 if wip:
1011 opts += ['wip']
1012 if opts:
1013 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001014 cmd.append(ref_spec)
1015
Anthony King7bdac712014-07-16 12:56:40 +01001016 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001017 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
Mike Frysingerd7f86832020-11-19 19:18:46 -05001019 if not dryrun:
1020 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1021 self.bare_git.UpdateRef(R_PUB + branch.name,
1022 R_HEADS + branch.name,
1023 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001024
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001025# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001026 def _ExtractArchive(self, tarpath, path=None):
1027 """Extract the given tar on its current location
1028
1029 Args:
1030 - tarpath: The path to the actual tar file
1031
1032 """
1033 try:
1034 with tarfile.open(tarpath, 'r') as tar:
1035 tar.extractall(path=path)
1036 return True
1037 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001038 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001039 return False
1040
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001041 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001042 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001043 verbose=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001044 is_new=None,
1045 current_branch_only=False,
1046 force_sync=False,
1047 clone_bundle=True,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001048 tags=True,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001049 archive=False,
1050 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001051 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001052 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001053 submodules=False,
1054 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001055 """Perform only the network IO portion of the sync process.
1056 Local working directory/branch state is not affected.
1057 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001058 if archive and not isinstance(self, MetaProject):
1059 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001060 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001061 return False
1062
1063 name = self.relpath.replace('\\', '/')
1064 name = name.replace('/', '_')
1065 tarpath = '%s.tar' % name
1066 topdir = self.manifest.topdir
1067
1068 try:
1069 self._FetchArchive(tarpath, cwd=topdir)
1070 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001071 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001072 return False
1073
1074 # From now on, we only need absolute tarpath
1075 tarpath = os.path.join(topdir, tarpath)
1076
1077 if not self._ExtractArchive(tarpath, path=topdir):
1078 return False
1079 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001080 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001081 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001082 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001083 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001084 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001085 if is_new is None:
1086 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001087 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001088 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001089 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001090 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001092
1093 if is_new:
1094 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1095 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001096 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001097 # This works for both absolute and relative alternate directories.
1098 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001099 except IOError:
1100 alt_dir = None
1101 else:
1102 alt_dir = None
1103
Mike Frysingere50b6a72020-02-19 01:45:48 -05001104 if (clone_bundle
1105 and alt_dir is None
1106 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001107 is_new = False
1108
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001109 if not current_branch_only:
1110 if self.sync_c:
1111 current_branch_only = True
1112 elif not self.manifest._loaded:
1113 # Manifest cannot check defaults until it syncs.
1114 current_branch_only = False
1115 elif self.manifest.default.sync_c:
1116 current_branch_only = True
1117
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001118 if not self.sync_tags:
1119 tags = False
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001120
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001121 if self.clone_depth:
1122 depth = self.clone_depth
1123 else:
1124 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1125
Mike Frysinger521d01b2020-02-17 01:51:49 -05001126 # See if we can skip the network fetch entirely.
1127 if not (optimized_fetch and
1128 (ID_RE.match(self.revisionExpr) and
1129 self._CheckForImmutableRevision())):
1130 if not self._RemoteFetch(
David Pursehouse3cceda52020-02-18 14:11:39 +09001131 initial=is_new, quiet=quiet, verbose=verbose, alt_dir=alt_dir,
1132 current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001133 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001134 submodules=submodules, force_sync=force_sync,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001135 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001136 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001137
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001138 mp = self.manifest.manifestProject
1139 dissociate = mp.config.GetBoolean('repo.dissociate')
1140 if dissociate:
1141 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1142 if os.path.exists(alternates_file):
1143 cmd = ['repack', '-a', '-d']
1144 if GitCommand(self, cmd, bare=True).Wait() != 0:
1145 return False
1146 platform_utils.remove(alternates_file)
1147
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001148 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001149 self._InitMRef()
1150 else:
1151 self._InitMirrorHead()
1152 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001153 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001154 except OSError:
1155 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001156 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001157
1158 def PostRepoUpgrade(self):
1159 self._InitHooks()
1160
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001161 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001162 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001163 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001164 for copyfile in self.copyfiles:
1165 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001166 for linkfile in self.linkfiles:
1167 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001168
Julien Camperguedd654222014-01-09 16:21:37 +01001169 def GetCommitRevisionId(self):
1170 """Get revisionId of a commit.
1171
1172 Use this method instead of GetRevisionId to get the id of the commit rather
1173 than the id of the current git object (for example, a tag)
1174
1175 """
1176 if not self.revisionExpr.startswith(R_TAGS):
1177 return self.GetRevisionId(self._allrefs)
1178
1179 try:
1180 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1181 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001182 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1183 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001184
David Pursehouse8a68ff92012-09-24 12:15:13 +09001185 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001186 if self.revisionId:
1187 return self.revisionId
1188
1189 rem = self.GetRemote(self.remote.name)
1190 rev = rem.ToLocal(self.revisionExpr)
1191
David Pursehouse8a68ff92012-09-24 12:15:13 +09001192 if all_refs is not None and rev in all_refs:
1193 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001194
1195 try:
1196 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1197 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001198 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1199 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001200
Raman Tenneti6a872c92021-01-14 19:17:50 -08001201 def SetRevisionId(self, revisionId):
1202 self.revisionId = revisionId
1203
Martin Kellye4e94d22017-03-21 16:05:12 -07001204 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001205 """Perform only the local IO portion of the sync process.
1206 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001207 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001208 if not os.path.exists(self.gitdir):
1209 syncbuf.fail(self,
1210 'Cannot checkout %s due to missing network sync; Run '
1211 '`repo sync -n %s` first.' %
1212 (self.name, self.name))
1213 return
1214
Martin Kellye4e94d22017-03-21 16:05:12 -07001215 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001216 all_refs = self.bare_ref.all
1217 self.CleanPublishedCache(all_refs)
1218 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001219
David Pursehouse1d947b32012-10-25 12:23:11 +09001220 def _doff():
1221 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001222 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001223
Martin Kellye4e94d22017-03-21 16:05:12 -07001224 def _dosubmodules():
1225 self._SyncSubmodules(quiet=True)
1226
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001227 head = self.work_git.GetHead()
1228 if head.startswith(R_HEADS):
1229 branch = head[len(R_HEADS):]
1230 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001231 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001232 except KeyError:
1233 head = None
1234 else:
1235 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001236
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001237 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001238 # Currently on a detached HEAD. The user is assumed to
1239 # not have any local modifications worth worrying about.
1240 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001241 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001242 syncbuf.fail(self, _PriorSyncFailedError())
1243 return
1244
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001245 if head == revid:
1246 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001247 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001248 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001249 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001250 # The copy/linkfile config may have changed.
1251 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001252 return
1253 else:
1254 lost = self._revlist(not_rev(revid), HEAD)
1255 if lost:
1256 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001257
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001258 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001259 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001260 if submodules:
1261 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001262 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001263 syncbuf.fail(self, e)
1264 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001265 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001266 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001267
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001268 if head == revid:
1269 # No changes; don't do anything further.
1270 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001271 # The copy/linkfile config may have changed.
1272 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001273 return
1274
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001275 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001276
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001277 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001278 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001279 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001280 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001281 syncbuf.info(self,
1282 "leaving %s; does not track upstream",
1283 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001284 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001285 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001286 if submodules:
1287 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001288 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001289 syncbuf.fail(self, e)
1290 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001291 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001292 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001293
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001294 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001295
1296 # See if we can perform a fast forward merge. This can happen if our
1297 # branch isn't in the exact same state as we last published.
1298 try:
1299 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1300 # Skip the published logic.
1301 pub = False
1302 except GitError:
1303 pub = self.WasPublished(branch.name, all_refs)
1304
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001305 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001306 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001307 if not_merged:
1308 if upstream_gain:
1309 # The user has published this branch and some of those
1310 # commits are not yet merged upstream. We do not want
1311 # to rewrite the published commits so we punt.
1312 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001313 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001314 "branch %s is published (but not merged) and is now "
1315 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001316 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001317 elif pub == head:
1318 # All published commits are merged, and thus we are a
1319 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001320 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001321 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001322 if submodules:
1323 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001324 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001325
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001326 # Examine the local commits not in the remote. Find the
1327 # last one attributed to this user, if any.
1328 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001329 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001330 last_mine = None
1331 cnt_mine = 0
1332 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001333 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001334 if committer_email == self.UserEmail:
1335 last_mine = commit_id
1336 cnt_mine += 1
1337
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001338 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001339 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001340
1341 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001342 syncbuf.fail(self, _DirtyError())
1343 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001344
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001345 # If the upstream switched on us, warn the user.
1346 #
1347 if branch.merge != self.revisionExpr:
1348 if branch.merge and self.revisionExpr:
1349 syncbuf.info(self,
1350 'manifest switched %s...%s',
1351 branch.merge,
1352 self.revisionExpr)
1353 elif branch.merge:
1354 syncbuf.info(self,
1355 'manifest no longer tracks %s',
1356 branch.merge)
1357
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001358 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001359 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001360 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001361 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001362 syncbuf.info(self,
1363 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001364 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001365
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001366 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001367 if not ID_RE.match(self.revisionExpr):
1368 # in case of manifest sync the revisionExpr might be a SHA1
1369 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001370 if not branch.merge.startswith('refs/'):
1371 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001372 branch.Save()
1373
Mike Pontillod3153822012-02-28 11:53:24 -08001374 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001375 def _docopyandlink():
1376 self._CopyAndLinkFiles()
1377
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001378 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001379 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001380 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001381 if submodules:
1382 syncbuf.later2(self, _dosubmodules)
1383 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001384 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001385 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001386 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001387 if submodules:
1388 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001389 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001390 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001391 syncbuf.fail(self, e)
1392 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001393 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001394 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001395 if submodules:
1396 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001397
Mike Frysingere6a202f2019-08-02 15:57:57 -04001398 def AddCopyFile(self, src, dest, topdir):
1399 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001400
Mike Frysingere6a202f2019-08-02 15:57:57 -04001401 No filesystem changes occur here. Actual copying happens later on.
1402
1403 Paths should have basic validation run on them before being queued.
1404 Further checking will be handled when the actual copy happens.
1405 """
1406 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1407
1408 def AddLinkFile(self, src, dest, topdir):
1409 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1410
1411 No filesystem changes occur here. Actual linking happens later on.
1412
1413 Paths should have basic validation run on them before being queued.
1414 Further checking will be handled when the actual link happens.
1415 """
1416 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001417
James W. Mills24c13082012-04-12 15:04:13 -05001418 def AddAnnotation(self, name, value, keep):
1419 self.annotations.append(_Annotation(name, value, keep))
1420
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001421 def DownloadPatchSet(self, change_id, patch_id):
1422 """Download a single patch set of a single change to FETCH_HEAD.
1423 """
1424 remote = self.GetRemote(self.remote.name)
1425
1426 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001427 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001428 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001429 if GitCommand(self, cmd, bare=True).Wait() != 0:
1430 return None
1431 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001432 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001433 change_id,
1434 patch_id,
1435 self.bare_git.rev_parse('FETCH_HEAD'))
1436
Mike Frysingerc0d18662020-02-19 19:19:18 -05001437 def DeleteWorktree(self, quiet=False, force=False):
1438 """Delete the source checkout and any other housekeeping tasks.
1439
1440 This currently leaves behind the internal .repo/ cache state. This helps
1441 when switching branches or manifest changes get reverted as we don't have
1442 to redownload all the git objects. But we should do some GC at some point.
1443
1444 Args:
1445 quiet: Whether to hide normal messages.
1446 force: Always delete tree even if dirty.
1447
1448 Returns:
1449 True if the worktree was completely cleaned out.
1450 """
1451 if self.IsDirty():
1452 if force:
1453 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1454 (self.relpath,), file=sys.stderr)
1455 else:
1456 print('error: %s: Cannot remove project: uncommitted changes are '
1457 'present.\n' % (self.relpath,), file=sys.stderr)
1458 return False
1459
1460 if not quiet:
1461 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1462
1463 # Unlock and delink from the main worktree. We don't use git's worktree
1464 # remove because it will recursively delete projects -- we handle that
1465 # ourselves below. https://crbug.com/git/48
1466 if self.use_git_worktrees:
1467 needle = platform_utils.realpath(self.gitdir)
1468 # Find the git worktree commondir under .repo/worktrees/.
1469 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1470 assert output.startswith('worktree '), output
1471 commondir = output[9:]
1472 # Walk each of the git worktrees to see where they point.
1473 configs = os.path.join(commondir, 'worktrees')
1474 for name in os.listdir(configs):
1475 gitdir = os.path.join(configs, name, 'gitdir')
1476 with open(gitdir) as fp:
1477 relpath = fp.read().strip()
1478 # Resolve the checkout path and see if it matches this project.
1479 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1480 if fullpath == needle:
1481 platform_utils.rmtree(os.path.join(configs, name))
1482
1483 # Delete the .git directory first, so we're less likely to have a partially
1484 # working git repository around. There shouldn't be any git projects here,
1485 # so rmtree works.
1486
1487 # Try to remove plain files first in case of git worktrees. If this fails
1488 # for any reason, we'll fall back to rmtree, and that'll display errors if
1489 # it can't remove things either.
1490 try:
1491 platform_utils.remove(self.gitdir)
1492 except OSError:
1493 pass
1494 try:
1495 platform_utils.rmtree(self.gitdir)
1496 except OSError as e:
1497 if e.errno != errno.ENOENT:
1498 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1499 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1500 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1501 return False
1502
1503 # Delete everything under the worktree, except for directories that contain
1504 # another git project.
1505 dirs_to_remove = []
1506 failed = False
1507 for root, dirs, files in platform_utils.walk(self.worktree):
1508 for f in files:
1509 path = os.path.join(root, f)
1510 try:
1511 platform_utils.remove(path)
1512 except OSError as e:
1513 if e.errno != errno.ENOENT:
1514 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1515 failed = True
1516 dirs[:] = [d for d in dirs
1517 if not os.path.lexists(os.path.join(root, d, '.git'))]
1518 dirs_to_remove += [os.path.join(root, d) for d in dirs
1519 if os.path.join(root, d) not in dirs_to_remove]
1520 for d in reversed(dirs_to_remove):
1521 if platform_utils.islink(d):
1522 try:
1523 platform_utils.remove(d)
1524 except OSError as e:
1525 if e.errno != errno.ENOENT:
1526 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1527 failed = True
1528 elif not platform_utils.listdir(d):
1529 try:
1530 platform_utils.rmdir(d)
1531 except OSError as e:
1532 if e.errno != errno.ENOENT:
1533 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1534 failed = True
1535 if failed:
1536 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1537 file=sys.stderr)
1538 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1539 return False
1540
1541 # Try deleting parent dirs if they are empty.
1542 path = self.worktree
1543 while path != self.manifest.topdir:
1544 try:
1545 platform_utils.rmdir(path)
1546 except OSError as e:
1547 if e.errno != errno.ENOENT:
1548 break
1549 path = os.path.dirname(path)
1550
1551 return True
1552
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001553# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001554 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 """Create a new branch off the manifest's revision.
1556 """
Simran Basib9a1b732015-08-20 12:19:28 -07001557 if not branch_merge:
1558 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001559 head = self.work_git.GetHead()
1560 if head == (R_HEADS + name):
1561 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562
David Pursehouse8a68ff92012-09-24 12:15:13 +09001563 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001564 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001565 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001566 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001567 capture_stdout=True,
1568 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001569
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001570 branch = self.GetBranch(name)
1571 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001572 branch.merge = branch_merge
1573 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1574 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001575
1576 if revision is None:
1577 revid = self.GetRevisionId(all_refs)
1578 else:
1579 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001580
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001581 if head.startswith(R_HEADS):
1582 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001583 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001584 except KeyError:
1585 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001586 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001587 ref = R_HEADS + name
1588 self.work_git.update_ref(ref, revid)
1589 self.work_git.symbolic_ref(HEAD, ref)
1590 branch.Save()
1591 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001592
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001593 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001594 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001595 capture_stdout=True,
1596 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001597 branch.Save()
1598 return True
1599 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001600
Wink Saville02d79452009-04-10 13:01:24 -07001601 def CheckoutBranch(self, name):
1602 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001603
1604 Args:
1605 name: The name of the branch to checkout.
1606
1607 Returns:
1608 True if the checkout succeeded; False if it didn't; None if the branch
1609 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001610 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001611 rev = R_HEADS + name
1612 head = self.work_git.GetHead()
1613 if head == rev:
1614 # Already on the branch
1615 #
1616 return True
Wink Saville02d79452009-04-10 13:01:24 -07001617
David Pursehouse8a68ff92012-09-24 12:15:13 +09001618 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001619 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001620 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001621 except KeyError:
1622 # Branch does not exist in this project
1623 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001624 return None
Wink Saville02d79452009-04-10 13:01:24 -07001625
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001626 if head.startswith(R_HEADS):
1627 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001628 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001629 except KeyError:
1630 head = None
1631
1632 if head == revid:
1633 # Same revision; just update HEAD to point to the new
1634 # target branch, but otherwise take no other action.
1635 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001636 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1637 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001638 return True
1639
1640 return GitCommand(self,
1641 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001642 capture_stdout=True,
1643 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001644
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001645 def AbandonBranch(self, name):
1646 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001647
1648 Args:
1649 name: The name of the branch to abandon.
1650
1651 Returns:
1652 True if the abandon succeeded; False if it didn't; None if the branch
1653 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001654 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001655 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001656 all_refs = self.bare_ref.all
1657 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001658 # Doesn't exist
1659 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001660
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001661 head = self.work_git.GetHead()
1662 if head == rev:
1663 # We can't destroy the branch while we are sitting
1664 # on it. Switch to a detached HEAD.
1665 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001667
David Pursehouse8a68ff92012-09-24 12:15:13 +09001668 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001669 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001670 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001671 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001672 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001673
1674 return GitCommand(self,
1675 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001676 capture_stdout=True,
1677 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001678
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001679 def PruneHeads(self):
1680 """Prune any topic branches already merged into upstream.
1681 """
1682 cb = self.CurrentBranch
1683 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001684 left = self._allrefs
1685 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001686 if name.startswith(R_HEADS):
1687 name = name[len(R_HEADS):]
1688 if cb is None or name != cb:
1689 kill.append(name)
1690
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001691 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001692 if cb is not None \
1693 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001694 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001695 self.work_git.DetachHead(HEAD)
1696 kill.append(cb)
1697
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001698 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001699 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001700
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001701 try:
1702 self.bare_git.DetachHead(rev)
1703
1704 b = ['branch', '-d']
1705 b.extend(kill)
1706 b = GitCommand(self, b, bare=True,
1707 capture_stdout=True,
1708 capture_stderr=True)
1709 b.Wait()
1710 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001711 if ID_RE.match(old):
1712 self.bare_git.DetachHead(old)
1713 else:
1714 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001715 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001716
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001717 for branch in kill:
1718 if (R_HEADS + branch) not in left:
1719 self.CleanPublishedCache()
1720 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001721
1722 if cb and cb not in kill:
1723 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001724 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001725
1726 kept = []
1727 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001728 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729 branch = self.GetBranch(branch)
1730 base = branch.LocalMerge
1731 if not base:
1732 base = rev
1733 kept.append(ReviewableBranch(self, branch, base))
1734 return kept
1735
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001736# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001737 def GetRegisteredSubprojects(self):
1738 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001739
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001740 def rec(subprojects):
1741 if not subprojects:
1742 return
1743 result.extend(subprojects)
1744 for p in subprojects:
1745 rec(p.subprojects)
1746 rec(self.subprojects)
1747 return result
1748
1749 def _GetSubmodules(self):
1750 # Unfortunately we cannot call `git submodule status --recursive` here
1751 # because the working tree might not exist yet, and it cannot be used
1752 # without a working tree in its current implementation.
1753
1754 def get_submodules(gitdir, rev):
1755 # Parse .gitmodules for submodule sub_paths and sub_urls
1756 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1757 if not sub_paths:
1758 return []
1759 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1760 # revision of submodule repository
1761 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1762 submodules = []
1763 for sub_path, sub_url in zip(sub_paths, sub_urls):
1764 try:
1765 sub_rev = sub_revs[sub_path]
1766 except KeyError:
1767 # Ignore non-exist submodules
1768 continue
1769 submodules.append((sub_rev, sub_path, sub_url))
1770 return submodules
1771
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001772 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1773 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001774
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001775 def parse_gitmodules(gitdir, rev):
1776 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1777 try:
Anthony King7bdac712014-07-16 12:56:40 +01001778 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1779 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001780 except GitError:
1781 return [], []
1782 if p.Wait() != 0:
1783 return [], []
1784
1785 gitmodules_lines = []
1786 fd, temp_gitmodules_path = tempfile.mkstemp()
1787 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001788 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001789 os.close(fd)
1790 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001791 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1792 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001793 if p.Wait() != 0:
1794 return [], []
1795 gitmodules_lines = p.stdout.split('\n')
1796 except GitError:
1797 return [], []
1798 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001799 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001800
1801 names = set()
1802 paths = {}
1803 urls = {}
1804 for line in gitmodules_lines:
1805 if not line:
1806 continue
1807 m = re_path.match(line)
1808 if m:
1809 names.add(m.group(1))
1810 paths[m.group(1)] = m.group(2)
1811 continue
1812 m = re_url.match(line)
1813 if m:
1814 names.add(m.group(1))
1815 urls[m.group(1)] = m.group(2)
1816 continue
1817 names = sorted(names)
1818 return ([paths.get(name, '') for name in names],
1819 [urls.get(name, '') for name in names])
1820
1821 def git_ls_tree(gitdir, rev, paths):
1822 cmd = ['ls-tree', rev, '--']
1823 cmd.extend(paths)
1824 try:
Anthony King7bdac712014-07-16 12:56:40 +01001825 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1826 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001827 except GitError:
1828 return []
1829 if p.Wait() != 0:
1830 return []
1831 objects = {}
1832 for line in p.stdout.split('\n'):
1833 if not line.strip():
1834 continue
1835 object_rev, object_path = line.split()[2:4]
1836 objects[object_path] = object_rev
1837 return objects
1838
1839 try:
1840 rev = self.GetRevisionId()
1841 except GitError:
1842 return []
1843 return get_submodules(self.gitdir, rev)
1844
1845 def GetDerivedSubprojects(self):
1846 result = []
1847 if not self.Exists:
1848 # If git repo does not exist yet, querying its submodules will
1849 # mess up its states; so return here.
1850 return result
1851 for rev, path, url in self._GetSubmodules():
1852 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001853 relpath, worktree, gitdir, objdir = \
1854 self.manifest.GetSubprojectPaths(self, name, path)
1855 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001856 if project:
1857 result.extend(project.GetDerivedSubprojects())
1858 continue
David James8d201162013-10-11 17:03:19 -07001859
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001860 if url.startswith('..'):
1861 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001862 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001863 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001864 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001865 review=self.remote.review,
1866 revision=self.remote.revision)
1867 subproject = Project(manifest=self.manifest,
1868 name=name,
1869 remote=remote,
1870 gitdir=gitdir,
1871 objdir=objdir,
1872 worktree=worktree,
1873 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001874 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001875 revisionId=rev,
1876 rebase=self.rebase,
1877 groups=self.groups,
1878 sync_c=self.sync_c,
1879 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001880 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001881 parent=self,
1882 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001883 result.append(subproject)
1884 result.extend(subproject.GetDerivedSubprojects())
1885 return result
1886
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001887# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001888 def EnableRepositoryExtension(self, key, value='true', version=1):
1889 """Enable git repository extension |key| with |value|.
1890
1891 Args:
1892 key: The extension to enabled. Omit the "extensions." prefix.
1893 value: The value to use for the extension.
1894 version: The minimum git repository version needed.
1895 """
1896 # Make sure the git repo version is new enough already.
1897 found_version = self.config.GetInt('core.repositoryFormatVersion')
1898 if found_version is None:
1899 found_version = 0
1900 if found_version < version:
1901 self.config.SetString('core.repositoryFormatVersion', str(version))
1902
1903 # Enable the extension!
1904 self.config.SetString('extensions.%s' % (key,), value)
1905
Mike Frysinger50a81de2020-09-06 15:51:21 -04001906 def ResolveRemoteHead(self, name=None):
1907 """Find out what the default branch (HEAD) points to.
1908
1909 Normally this points to refs/heads/master, but projects are moving to main.
1910 Support whatever the server uses rather than hardcoding "master" ourselves.
1911 """
1912 if name is None:
1913 name = self.remote.name
1914
1915 # The output will look like (NB: tabs are separators):
1916 # ref: refs/heads/master HEAD
1917 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1918 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1919
1920 for line in output.splitlines():
1921 lhs, rhs = line.split('\t', 1)
1922 if rhs == 'HEAD' and lhs.startswith('ref:'):
1923 return lhs[4:].strip()
1924
1925 return None
1926
Zac Livingstone4332262017-06-16 08:56:09 -06001927 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001928 try:
1929 # if revision (sha or tag) is not present then following function
1930 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001931 self.bare_git.rev_list('-1', '--missing=allow-any',
1932 '%s^0' % self.revisionExpr, '--')
Chris AtLee2fb64662014-01-16 21:32:33 -05001933 return True
1934 except GitError:
1935 # There is no such persistent revision. We have to fetch it.
1936 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001937
Julien Campergue335f5ef2013-10-16 11:02:35 +02001938 def _FetchArchive(self, tarpath, cwd=None):
1939 cmd = ['archive', '-v', '-o', tarpath]
1940 cmd.append('--remote=%s' % self.remote.url)
1941 cmd.append('--prefix=%s/' % self.relpath)
1942 cmd.append(self.revisionExpr)
1943
1944 command = GitCommand(self, cmd, cwd=cwd,
1945 capture_stdout=True,
1946 capture_stderr=True)
1947
1948 if command.Wait() != 0:
1949 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1950
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001951 def _RemoteFetch(self, name=None,
1952 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001953 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001954 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001955 verbose=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001956 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001957 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001958 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001959 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001960 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001961 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001962 clone_filter=None,
1963 retry_fetches=2,
1964 retry_sleep_initial_sec=4.0,
1965 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001966 is_sha1 = False
1967 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001968 # The depth should not be used when fetching to a mirror because
1969 # it will result in a shallow repository that cannot be cloned or
1970 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001971 # The repo project should also never be synced with partial depth.
1972 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1973 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001974
Shawn Pearce69e04d82014-01-29 12:48:54 -08001975 if depth:
1976 current_branch_only = True
1977
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001978 if ID_RE.match(self.revisionExpr) is not None:
1979 is_sha1 = True
1980
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001981 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001982 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001983 # this is a tag and its sha1 value should never change
1984 tag_name = self.revisionExpr[len(R_TAGS):]
1985
1986 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001987 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05001988 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001989 print('Skipped fetching project %s (already have persistent ref)'
1990 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001991 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001992 if is_sha1 and not depth:
1993 # When syncing a specific commit and --depth is not set:
1994 # * if upstream is explicitly specified and is not a sha1, fetch only
1995 # upstream as users expect only upstream to be fetch.
1996 # Note: The commit might not be in upstream in which case the sync
1997 # will fail.
1998 # * otherwise, fetch all branches to make sure we end up with the
1999 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002000 if self.upstream:
2001 current_branch_only = not ID_RE.match(self.upstream)
2002 else:
2003 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002004
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002005 if not name:
2006 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002007
2008 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002009 remote = self.GetRemote(name)
2010 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002011 ssh_proxy = True
2012
Shawn O. Pearce88443382010-10-08 10:02:09 +02002013 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002014 if alt_dir and 'objects' == os.path.basename(alt_dir):
2015 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002016 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2017 remote = self.GetRemote(name)
2018
David Pursehouse8a68ff92012-09-24 12:15:13 +09002019 all_refs = self.bare_ref.all
2020 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002021 tmp = set()
2022
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302023 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002024 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002025 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002026 all_refs[r] = ref_id
2027 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002028 continue
2029
David Pursehouse8a68ff92012-09-24 12:15:13 +09002030 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002031 continue
2032
David Pursehouse8a68ff92012-09-24 12:15:13 +09002033 r = 'refs/_alt/%s' % ref_id
2034 all_refs[r] = ref_id
2035 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002036 tmp.add(r)
2037
heping3d7bbc92017-04-12 19:51:47 +08002038 tmp_packed_lines = []
2039 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002040
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302041 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002042 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002043 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002044 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002045 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002046
heping3d7bbc92017-04-12 19:51:47 +08002047 tmp_packed = ''.join(tmp_packed_lines)
2048 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002049 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002051 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002052
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002053 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002054
Xin Li745be2e2019-06-03 11:24:30 -07002055 if clone_filter:
2056 git_require((2, 19, 0), fail=True, msg='partial clones')
2057 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002058 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002059
Conley Owensf97e8382015-01-21 11:12:46 -08002060 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002061 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002062 else:
2063 # If this repo has shallow objects, then we don't know which refs have
2064 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2065 # do this with projects that don't have shallow objects, since it is less
2066 # efficient.
2067 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2068 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002069
Mike Frysinger4847e052020-02-22 00:07:35 -05002070 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002071 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002072 if not quiet and sys.stdout.isatty():
2073 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002074 if not self.worktree:
2075 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002076 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002077
Mike Frysingere57f1142019-03-18 21:27:54 -04002078 if force_sync:
2079 cmd.append('--force')
2080
David Pursehouse74cfd272015-10-14 10:50:15 +09002081 if prune:
2082 cmd.append('--prune')
2083
Martin Kellye4e94d22017-03-21 16:05:12 -07002084 if submodules:
2085 cmd.append('--recurse-submodules=on-demand')
2086
Kuang-che Wu6856f982019-11-25 12:37:55 +08002087 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002088 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002089 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002090 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002091 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002092 spec.append('tag')
2093 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002094
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302095 if self.manifest.IsMirror and not current_branch_only:
2096 branch = None
2097 else:
2098 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002099 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002100 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002101 # Shallow checkout of a specific commit, fetch from that commit and not
2102 # the heads only as the commit might be deeper in the history.
2103 spec.append(branch)
2104 else:
2105 if is_sha1:
2106 branch = self.upstream
2107 if branch is not None and branch.strip():
2108 if not branch.startswith('refs/'):
2109 branch = R_HEADS + branch
2110 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2111
2112 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2113 # whole repo.
2114 if self.manifest.IsMirror and not spec:
2115 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2116
2117 # If using depth then we should not get all the tags since they may
2118 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002119 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002120 cmd.append('--no-tags')
2121 else:
2122 cmd.append('--tags')
2123 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2124
Conley Owens80b87fe2014-05-09 17:13:44 -07002125 cmd.extend(spec)
2126
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002127 # At least one retry minimum due to git remote prune.
2128 retry_fetches = max(retry_fetches, 2)
2129 retry_cur_sleep = retry_sleep_initial_sec
2130 ok = prune_tried = False
2131 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002132 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger4847e052020-02-22 00:07:35 -05002133 merge_output=True, capture_stdout=quiet)
John L. Villalovos126e2982015-01-29 21:58:12 -08002134 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002135 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002136 ok = True
2137 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002138
2139 # Retry later due to HTTP 429 Too Many Requests.
2140 elif ('error:' in gitcmd.stderr and
2141 'HTTP 429' in gitcmd.stderr):
2142 if not quiet:
2143 print('429 received, sleeping: %s sec' % retry_cur_sleep,
2144 file=sys.stderr)
2145 time.sleep(retry_cur_sleep)
2146 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2147 MAXIMUM_RETRY_SLEEP_SEC)
2148 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2149 RETRY_JITTER_PERCENT))
2150 continue
2151
2152 # If this is not last attempt, try 'git remote prune'.
2153 elif (try_n < retry_fetches - 1 and
2154 'error:' in gitcmd.stderr and
2155 'git remote prune' in gitcmd.stderr and
2156 not prune_tried):
2157 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002158 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002159 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002160 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002161 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002162 break
2163 continue
Brian Harring14a66742012-09-28 20:21:57 -07002164 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002165 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2166 # in sha1 mode, we just tried sync'ing from the upstream field; it
2167 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002168 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002169 elif ret < 0:
2170 # Git died with a signal, exit immediately
2171 break
Mike Frysinger31990f02020-02-17 01:35:18 -05002172 if not verbose:
2173 print('%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002174 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002175
2176 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002177 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002178 if old_packed != '':
2179 _lwrite(packed_refs, old_packed)
2180 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002181 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002182 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002183
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002184 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002185 # We just synced the upstream given branch; verify we
2186 # got what we wanted, else trigger a second run of all
2187 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002188 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002189 # Sync the current branch only with depth set to None.
2190 # We always pass depth=None down to avoid infinite recursion.
2191 return self._RemoteFetch(
2192 name=name, quiet=quiet, verbose=verbose,
2193 current_branch_only=current_branch_only and depth,
2194 initial=False, alt_dir=alt_dir,
2195 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002196
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002197 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002198
Mike Frysingere50b6a72020-02-19 01:45:48 -05002199 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002200 if initial and \
2201 (self.manifest.manifestProject.config.GetString('repo.depth') or
2202 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002203 return False
2204
2205 remote = self.GetRemote(self.remote.name)
2206 bundle_url = remote.url + '/clone.bundle'
2207 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002208 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2209 'persistent-http',
2210 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002211 return False
2212
2213 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2214 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2215
2216 exist_dst = os.path.exists(bundle_dst)
2217 exist_tmp = os.path.exists(bundle_tmp)
2218
2219 if not initial and not exist_dst and not exist_tmp:
2220 return False
2221
2222 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002223 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2224 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002225 if not exist_dst:
2226 return False
2227
2228 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002229 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002230 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002231 if not quiet and sys.stdout.isatty():
2232 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002233 if not self.worktree:
2234 cmd.append('--update-head-ok')
2235 cmd.append(bundle_dst)
2236 for f in remote.fetch:
2237 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002238 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002239
2240 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002241 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002242 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002243 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002244 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002245 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002246
Mike Frysingere50b6a72020-02-19 01:45:48 -05002247 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002248 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002249 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002251 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002252 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002253 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002254 if os.path.exists(tmpPath):
2255 size = os.stat(tmpPath).st_size
2256 if size >= 1024:
2257 cmd += ['--continue-at', '%d' % (size,)]
2258 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002259 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002260 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002261 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002262 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002263 if proxy:
2264 cmd += ['--proxy', proxy]
2265 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2266 cmd += ['--proxy', os.environ['http_proxy']]
2267 if srcUrl.startswith('persistent-https'):
2268 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2269 elif srcUrl.startswith('persistent-http'):
2270 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002271 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002272
Dave Borowitz137d0132015-01-02 11:12:54 -08002273 if IsTrace():
2274 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002275 if verbose:
2276 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2277 stdout = None if verbose else subprocess.PIPE
2278 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002279 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002280 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002281 except OSError:
2282 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002283
Mike Frysingere50b6a72020-02-19 01:45:48 -05002284 (output, _) = proc.communicate()
2285 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002286
Dave Borowitz137d0132015-01-02 11:12:54 -08002287 if curlret == 22:
2288 # From curl man page:
2289 # 22: HTTP page not retrieved. The requested url was not found or
2290 # returned another error with the HTTP error code being 400 or above.
2291 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002292 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002293 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2294 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002295 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002296 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002297 elif curlret and not verbose and output:
2298 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002299
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002300 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002301 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002302 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002303 return True
2304 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002305 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002306 return False
2307 else:
2308 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002309
Kris Giesingc8d882a2014-12-23 13:02:32 -08002310 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002311 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002312 with open(path, 'rb') as f:
2313 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002314 return True
2315 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002316 if not quiet:
2317 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002318 return False
2319 except OSError:
2320 return False
2321
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002322 def _Checkout(self, rev, quiet=False):
2323 cmd = ['checkout']
2324 if quiet:
2325 cmd.append('-q')
2326 cmd.append(rev)
2327 cmd.append('--')
2328 if GitCommand(self, cmd).Wait() != 0:
2329 if self._allrefs:
2330 raise GitError('%s checkout %s ' % (self.name, rev))
2331
Mike Frysinger915fda12020-03-22 12:15:20 -04002332 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002333 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002334 if ffonly:
2335 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002336 if record_origin:
2337 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002338 cmd.append(rev)
2339 cmd.append('--')
2340 if GitCommand(self, cmd).Wait() != 0:
2341 if self._allrefs:
2342 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2343
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302344 def _LsRemote(self, refs):
2345 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302346 p = GitCommand(self, cmd, capture_stdout=True)
2347 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002348 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302349 return None
2350
Anthony King7bdac712014-07-16 12:56:40 +01002351 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002352 cmd = ['revert']
2353 cmd.append('--no-edit')
2354 cmd.append(rev)
2355 cmd.append('--')
2356 if GitCommand(self, cmd).Wait() != 0:
2357 if self._allrefs:
2358 raise GitError('%s revert %s ' % (self.name, rev))
2359
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002360 def _ResetHard(self, rev, quiet=True):
2361 cmd = ['reset', '--hard']
2362 if quiet:
2363 cmd.append('-q')
2364 cmd.append(rev)
2365 if GitCommand(self, cmd).Wait() != 0:
2366 raise GitError('%s reset --hard %s ' % (self.name, rev))
2367
Martin Kellye4e94d22017-03-21 16:05:12 -07002368 def _SyncSubmodules(self, quiet=True):
2369 cmd = ['submodule', 'update', '--init', '--recursive']
2370 if quiet:
2371 cmd.append('-q')
2372 if GitCommand(self, cmd).Wait() != 0:
2373 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2374
Anthony King7bdac712014-07-16 12:56:40 +01002375 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002376 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002377 if onto is not None:
2378 cmd.extend(['--onto', onto])
2379 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002380 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002381 raise GitError('%s rebase %s ' % (self.name, upstream))
2382
Pierre Tardy3d125942012-05-04 12:18:12 +02002383 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002384 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002385 if ffonly:
2386 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002387 if GitCommand(self, cmd).Wait() != 0:
2388 raise GitError('%s merge %s ' % (self.name, head))
2389
David Pursehousee8ace262020-02-13 12:41:15 +09002390 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002391 init_git_dir = not os.path.exists(self.gitdir)
2392 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002393 try:
2394 # Initialize the bare repository, which contains all of the objects.
2395 if init_obj_dir:
2396 os.makedirs(self.objdir)
2397 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002398
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002399 if self.use_git_worktrees:
2400 # Set up the m/ space to point to the worktree-specific ref space.
2401 # We'll update the worktree-specific ref space on each checkout.
2402 if self.manifest.branch:
2403 self.bare_git.symbolic_ref(
2404 '-m', 'redirecting to worktree scope',
2405 R_M + self.manifest.branch,
2406 R_WORKTREE_M + self.manifest.branch)
2407
2408 # Enable per-worktree config file support if possible. This is more a
2409 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002410 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002411 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002412
Kevin Degib1a07b82015-07-27 13:33:43 -06002413 # If we have a separate directory to hold refs, initialize it as well.
2414 if self.objdir != self.gitdir:
2415 if init_git_dir:
2416 os.makedirs(self.gitdir)
2417
2418 if init_obj_dir or init_git_dir:
2419 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2420 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002421 try:
2422 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2423 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002424 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002425 print("Retrying clone after deleting %s" %
2426 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002427 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002428 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2429 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002430 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002431 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002432 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2433 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002434 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002435 raise e
2436 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002437
Kevin Degi384b3c52014-10-16 16:02:58 -06002438 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002439 mp = self.manifest.manifestProject
2440 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002441
Kevin Degib1a07b82015-07-27 13:33:43 -06002442 if ref_dir or mirror_git:
2443 if not mirror_git:
2444 mirror_git = os.path.join(ref_dir, self.name + '.git')
2445 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2446 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002447 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2448 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002449
Kevin Degib1a07b82015-07-27 13:33:43 -06002450 if os.path.exists(mirror_git):
2451 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002452 elif os.path.exists(repo_git):
2453 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002454 elif os.path.exists(worktrees_git):
2455 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002456 else:
2457 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002458
Kevin Degib1a07b82015-07-27 13:33:43 -06002459 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002460 if not os.path.isabs(ref_dir):
2461 # The alternate directory is relative to the object database.
2462 ref_dir = os.path.relpath(ref_dir,
2463 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002464 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2465 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002466
David Pursehousee8ace262020-02-13 12:41:15 +09002467 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002468
2469 m = self.manifest.manifestProject.config
2470 for key in ['user.name', 'user.email']:
2471 if m.Has(key, include_defaults=False):
2472 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002473 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002474 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002475 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002476 except Exception:
2477 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002478 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002479 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002480 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002481 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002482
David Pursehousee8ace262020-02-13 12:41:15 +09002483 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002484 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002485 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002486
David Pursehousee8ace262020-02-13 12:41:15 +09002487 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002488 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002489 if not os.path.exists(hooks):
2490 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002491 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002492 name = os.path.basename(stock_hook)
2493
Victor Boivie65e0f352011-04-18 11:23:29 +02002494 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002495 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002496 # Don't install a Gerrit Code Review hook if this
2497 # project does not appear to use it for reviews.
2498 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002499 # Since the manifest project is one of those, but also
2500 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002501 continue
2502
2503 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002504 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002505 continue
2506 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002507 # If the files are the same, we'll leave it alone. We create symlinks
2508 # below by default but fallback to hardlinks if the OS blocks them.
2509 # So if we're here, it's probably because we made a hardlink below.
2510 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002511 if not quiet:
2512 _warn("%s: Not replacing locally modified %s hook",
2513 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002514 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002515 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002516 platform_utils.symlink(
2517 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002518 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002519 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002520 try:
2521 os.link(stock_hook, dst)
2522 except OSError:
2523 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002524 else:
2525 raise
2526
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002527 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002528 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002529 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002530 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002531 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002532 remote.review = self.remote.review
2533 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002534
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002535 if self.worktree:
2536 remote.ResetFetch(mirror=False)
2537 else:
2538 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539 remote.Save()
2540
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002541 def _InitMRef(self):
2542 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002543 if self.use_git_worktrees:
2544 # We can't update this ref with git worktrees until it exists.
2545 # We'll wait until the initial checkout to set it.
2546 if not os.path.exists(self.worktree):
2547 return
2548
2549 base = R_WORKTREE_M
2550 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002551
2552 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002553 else:
2554 base = R_M
2555 active_git = self.bare_git
2556
2557 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002558
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002559 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002560 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002561
Remy Böhmer1469c282020-12-15 18:49:02 +01002562 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002563 cur = self.bare_ref.symref(ref)
2564
2565 if self.revisionId:
2566 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2567 msg = 'manifest set to %s' % self.revisionId
2568 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002569 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002570 else:
2571 remote = self.GetRemote(self.remote.name)
2572 dst = remote.ToLocal(self.revisionExpr)
2573 if cur != dst:
2574 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002575 if detach:
2576 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2577 else:
2578 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002579
Kevin Degi384b3c52014-10-16 16:02:58 -06002580 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002581 # Git worktrees don't use symlinks to share at all.
2582 if self.use_git_worktrees:
2583 return
2584
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002585 symlink_files = self.shareable_files[:]
2586 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002587 if share_refs:
2588 symlink_files += self.working_tree_files
2589 symlink_dirs += self.working_tree_dirs
2590 to_symlink = symlink_files + symlink_dirs
2591 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002592 # Try to self-heal a bit in simple cases.
2593 dst_path = os.path.join(destdir, name)
2594 src_path = os.path.join(srcdir, name)
2595
2596 if name in self.working_tree_dirs:
2597 # If the dir is missing under .repo/projects/, create it.
2598 if not os.path.exists(src_path):
2599 os.makedirs(src_path)
2600
2601 elif name in self.working_tree_files:
2602 # If it's a file under the checkout .git/ and the .repo/projects/ has
2603 # nothing, move the file under the .repo/projects/ tree.
2604 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2605 platform_utils.rename(dst_path, src_path)
2606
2607 # If the path exists under the .repo/projects/ and there's no symlink
2608 # under the checkout .git/, recreate the symlink.
2609 if name in self.working_tree_dirs or name in self.working_tree_files:
2610 if os.path.exists(src_path) and not os.path.exists(dst_path):
2611 platform_utils.symlink(
2612 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2613
2614 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002615 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002616 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002617 # Fail if the links are pointing to the wrong place
2618 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002619 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002620 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002621 'work tree. If you\'re comfortable with the '
2622 'possibility of losing the work tree\'s git metadata,'
2623 ' use `repo sync --force-sync {0}` to '
2624 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002625
David James8d201162013-10-11 17:03:19 -07002626 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2627 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2628
2629 Args:
2630 gitdir: The bare git repository. Must already be initialized.
2631 dotgit: The repository you would like to initialize.
2632 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2633 Only one work tree can store refs under a given |gitdir|.
2634 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2635 This saves you the effort of initializing |dotgit| yourself.
2636 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002637 symlink_files = self.shareable_files[:]
2638 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002639 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002640 symlink_files += self.working_tree_files
2641 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002642 to_symlink = symlink_files + symlink_dirs
2643
2644 to_copy = []
2645 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002646 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002647
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002648 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002649 for name in set(to_copy).union(to_symlink):
2650 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002651 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002652 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002653
Kevin Degi384b3c52014-10-16 16:02:58 -06002654 if os.path.lexists(dst):
2655 continue
David James8d201162013-10-11 17:03:19 -07002656
2657 # If the source dir doesn't exist, create an empty dir.
2658 if name in symlink_dirs and not os.path.lexists(src):
2659 os.makedirs(src)
2660
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002661 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002662 platform_utils.symlink(
2663 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002664 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002665 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002666 shutil.copytree(src, dst)
2667 elif os.path.isfile(src):
2668 shutil.copy(src, dst)
2669
Conley Owens80b87fe2014-05-09 17:13:44 -07002670 # If the source file doesn't exist, ensure the destination
2671 # file doesn't either.
2672 if name in symlink_files and not os.path.lexists(src):
2673 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002674 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002675 except OSError:
2676 pass
2677
David James8d201162013-10-11 17:03:19 -07002678 except OSError as e:
2679 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002680 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002681 else:
2682 raise
2683
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002684 def _InitGitWorktree(self):
2685 """Init the project using git worktrees."""
2686 self.bare_git.worktree('prune')
2687 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2688 self.worktree, self.GetRevisionId())
2689
2690 # Rewrite the internal state files to use relative paths between the
2691 # checkouts & worktrees.
2692 dotgit = os.path.join(self.worktree, '.git')
2693 with open(dotgit, 'r') as fp:
2694 # Figure out the checkout->worktree path.
2695 setting = fp.read()
2696 assert setting.startswith('gitdir:')
2697 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002698 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2699 # of file permissions. Delete it and recreate it from scratch to avoid.
2700 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002701 # Use relative path from checkout->worktree & maintain Unix line endings
2702 # on all OS's to match git behavior.
2703 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002704 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2705 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002706 # Use relative path from worktree->checkout & maintain Unix line endings
2707 # on all OS's to match git behavior.
2708 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002709 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2710
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002711 self._InitMRef()
2712
Martin Kellye4e94d22017-03-21 16:05:12 -07002713 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002714 realdotgit = os.path.join(self.worktree, '.git')
2715 tmpdotgit = realdotgit + '.tmp'
2716 init_dotgit = not os.path.exists(realdotgit)
2717 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002718 if self.use_git_worktrees:
2719 self._InitGitWorktree()
2720 self._CopyAndLinkFiles()
2721 return
2722
Mike Frysingerf4545122019-11-11 04:34:16 -05002723 dotgit = tmpdotgit
2724 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2725 os.makedirs(tmpdotgit)
2726 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2727 copy_all=False)
2728 else:
2729 dotgit = realdotgit
2730
Kevin Degib1a07b82015-07-27 13:33:43 -06002731 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002732 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2733 except GitError as e:
2734 if force_sync and not init_dotgit:
2735 try:
2736 platform_utils.rmtree(dotgit)
2737 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002738 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002739 raise e
2740 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741
Mike Frysingerf4545122019-11-11 04:34:16 -05002742 if init_dotgit:
2743 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002744
Mike Frysingerf4545122019-11-11 04:34:16 -05002745 # Now that the .git dir is fully set up, move it to its final home.
2746 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002747
Mike Frysingerf4545122019-11-11 04:34:16 -05002748 # Finish checking out the worktree.
2749 cmd = ['read-tree', '--reset', '-u']
2750 cmd.append('-v')
2751 cmd.append(HEAD)
2752 if GitCommand(self, cmd).Wait() != 0:
2753 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002754
Mike Frysingerf4545122019-11-11 04:34:16 -05002755 if submodules:
2756 self._SyncSubmodules(quiet=True)
2757 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002758
Renaud Paquay788e9622017-01-27 11:41:12 -08002759 def _get_symlink_error_message(self):
2760 if platform_utils.isWindows():
2761 return ('Unable to create symbolic link. Please re-run the command as '
2762 'Administrator, or see '
2763 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2764 'for other options.')
2765 return 'filesystem must support symlinks'
2766
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002767 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002768 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002769
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002770 def _revlist(self, *args, **kw):
2771 a = []
2772 a.extend(args)
2773 a.append('--')
2774 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002775
2776 @property
2777 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002778 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002780 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002781 """Get logs between two revisions of this project."""
2782 comp = '..'
2783 if rev1:
2784 revs = [rev1]
2785 if rev2:
2786 revs.extend([comp, rev2])
2787 cmd = ['log', ''.join(revs)]
2788 out = DiffColoring(self.config)
2789 if out.is_on and color:
2790 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002791 if pretty_format is not None:
2792 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002793 if oneline:
2794 cmd.append('--oneline')
2795
2796 try:
2797 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2798 if log.Wait() == 0:
2799 return log.stdout
2800 except GitError:
2801 # worktree may not exist if groups changed for example. In that case,
2802 # try in gitdir instead.
2803 if not os.path.exists(self.worktree):
2804 return self.bare_git.log(*cmd[1:])
2805 else:
2806 raise
2807 return None
2808
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002809 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2810 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002811 """Get the list of logs from this revision to given revisionId"""
2812 logs = {}
2813 selfId = self.GetRevisionId(self._allrefs)
2814 toId = toProject.GetRevisionId(toProject._allrefs)
2815
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002816 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2817 pretty_format=pretty_format)
2818 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2819 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002820 return logs
2821
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002822 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002823
David James8d201162013-10-11 17:03:19 -07002824 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002825 self._project = project
2826 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002827 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002829 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2830 def __getstate__(self):
2831 return (self._project, self._bare, self._gitdir)
2832
2833 def __setstate__(self, state):
2834 self._project, self._bare, self._gitdir = state
2835
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836 def LsOthers(self):
2837 p = GitCommand(self._project,
2838 ['ls-files',
2839 '-z',
2840 '--others',
2841 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002842 bare=False,
David James8d201162013-10-11 17:03:19 -07002843 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002844 capture_stdout=True,
2845 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002846 if p.Wait() == 0:
2847 out = p.stdout
2848 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002849 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002850 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002851 return []
2852
2853 def DiffZ(self, name, *args):
2854 cmd = [name]
2855 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002856 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 cmd.extend(args)
2858 p = GitCommand(self._project,
2859 cmd,
David James8d201162013-10-11 17:03:19 -07002860 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002861 bare=False,
2862 capture_stdout=True,
2863 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002864 try:
2865 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002866 if not hasattr(out, 'encode'):
2867 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002868 r = {}
2869 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002870 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002871 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002872 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002873 info = next(out)
2874 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002875 except StopIteration:
2876 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002877
2878 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002879
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002880 def __init__(self, path, omode, nmode, oid, nid, state):
2881 self.path = path
2882 self.src_path = None
2883 self.old_mode = omode
2884 self.new_mode = nmode
2885 self.old_id = oid
2886 self.new_id = nid
2887
2888 if len(state) == 1:
2889 self.status = state
2890 self.level = None
2891 else:
2892 self.status = state[:1]
2893 self.level = state[1:]
2894 while self.level.startswith('0'):
2895 self.level = self.level[1:]
2896
2897 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002898 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002899 if info.status in ('R', 'C'):
2900 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002901 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002902 r[info.path] = info
2903 return r
2904 finally:
2905 p.Wait()
2906
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002907 def GetDotgitPath(self, subpath=None):
2908 """Return the full path to the .git dir.
2909
2910 As a convenience, append |subpath| if provided.
2911 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002912 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002913 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002914 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002915 dotgit = os.path.join(self._project.worktree, '.git')
2916 if os.path.isfile(dotgit):
2917 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2918 with open(dotgit) as fp:
2919 setting = fp.read()
2920 assert setting.startswith('gitdir:')
2921 gitdir = setting.split(':', 1)[1].strip()
2922 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2923
2924 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2925
2926 def GetHead(self):
2927 """Return the ref that HEAD points to."""
2928 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002929 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002930 with open(path) as fd:
2931 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002932 except IOError as e:
2933 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002934 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302935 line = line.decode()
2936 except AttributeError:
2937 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002938 if line.startswith('ref: '):
2939 return line[5:-1]
2940 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002941
2942 def SetHead(self, ref, message=None):
2943 cmdv = []
2944 if message is not None:
2945 cmdv.extend(['-m', message])
2946 cmdv.append(HEAD)
2947 cmdv.append(ref)
2948 self.symbolic_ref(*cmdv)
2949
2950 def DetachHead(self, new, message=None):
2951 cmdv = ['--no-deref']
2952 if message is not None:
2953 cmdv.extend(['-m', message])
2954 cmdv.append(HEAD)
2955 cmdv.append(new)
2956 self.update_ref(*cmdv)
2957
2958 def UpdateRef(self, name, new, old=None,
2959 message=None,
2960 detach=False):
2961 cmdv = []
2962 if message is not None:
2963 cmdv.extend(['-m', message])
2964 if detach:
2965 cmdv.append('--no-deref')
2966 cmdv.append(name)
2967 cmdv.append(new)
2968 if old is not None:
2969 cmdv.append(old)
2970 self.update_ref(*cmdv)
2971
2972 def DeleteRef(self, name, old=None):
2973 if not old:
2974 old = self.rev_parse(name)
2975 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002976 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002977
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002978 def rev_list(self, *args, **kw):
2979 if 'format' in kw:
2980 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2981 else:
2982 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002983 cmdv.extend(args)
2984 p = GitCommand(self._project,
2985 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002986 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002987 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002988 capture_stdout=True,
2989 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002990 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002991 raise GitError('%s rev-list %s: %s' %
2992 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002993 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002994
2995 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002996 """Allow arbitrary git commands using pythonic syntax.
2997
2998 This allows you to do things like:
2999 git_obj.rev_parse('HEAD')
3000
3001 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3002 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003003 Any other positional arguments will be passed to the git command, and the
3004 following keyword arguments are supported:
3005 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003006
3007 Args:
3008 name: The name of the git command to call. Any '_' characters will
3009 be replaced with '-'.
3010
3011 Returns:
3012 A callable object that will try to call git with the named command.
3013 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003014 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003015
Dave Borowitz091f8932012-10-23 17:01:04 -07003016 def runner(*args, **kwargs):
3017 cmdv = []
3018 config = kwargs.pop('config', None)
3019 for k in kwargs:
3020 raise TypeError('%s() got an unexpected keyword argument %r'
3021 % (name, k))
3022 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303023 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003024 cmdv.append('-c')
3025 cmdv.append('%s=%s' % (k, v))
3026 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003027 cmdv.extend(args)
3028 p = GitCommand(self._project,
3029 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003030 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003031 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003032 capture_stdout=True,
3033 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003034 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003035 raise GitError('%s %s: %s' %
3036 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003037 r = p.stdout
3038 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3039 return r[:-1]
3040 return r
3041 return runner
3042
3043
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003044class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003045
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003046 def __str__(self):
3047 return 'prior sync failed; rebase still in progress'
3048
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003049
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003050class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003051
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003052 def __str__(self):
3053 return 'contains uncommitted changes'
3054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003055
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003056class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003057
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003058 def __init__(self, project, text):
3059 self.project = project
3060 self.text = text
3061
3062 def Print(self, syncbuf):
3063 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3064 syncbuf.out.nl()
3065
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003066
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003067class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003068
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003069 def __init__(self, project, why):
3070 self.project = project
3071 self.why = why
3072
3073 def Print(self, syncbuf):
3074 syncbuf.out.fail('error: %s/: %s',
3075 self.project.relpath,
3076 str(self.why))
3077 syncbuf.out.nl()
3078
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003079
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003080class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003081
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003082 def __init__(self, project, action):
3083 self.project = project
3084 self.action = action
3085
3086 def Run(self, syncbuf):
3087 out = syncbuf.out
3088 out.project('project %s/', self.project.relpath)
3089 out.nl()
3090 try:
3091 self.action()
3092 out.nl()
3093 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003094 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003095 out.nl()
3096 return False
3097
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003098
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003099class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003100
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003101 def __init__(self, config):
3102 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003103 self.project = self.printer('header', attr='bold')
3104 self.info = self.printer('info')
3105 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003107
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003108class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003109
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003110 def __init__(self, config, detach_head=False):
3111 self._messages = []
3112 self._failures = []
3113 self._later_queue1 = []
3114 self._later_queue2 = []
3115
3116 self.out = _SyncColoring(config)
3117 self.out.redirect(sys.stderr)
3118
3119 self.detach_head = detach_head
3120 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003121 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003122
3123 def info(self, project, fmt, *args):
3124 self._messages.append(_InfoMessage(project, fmt % args))
3125
3126 def fail(self, project, err=None):
3127 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003128 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003129
3130 def later1(self, project, what):
3131 self._later_queue1.append(_Later(project, what))
3132
3133 def later2(self, project, what):
3134 self._later_queue2.append(_Later(project, what))
3135
3136 def Finish(self):
3137 self._PrintMessages()
3138 self._RunLater()
3139 self._PrintMessages()
3140 return self.clean
3141
David Rileye0684ad2017-04-05 00:02:59 -07003142 def Recently(self):
3143 recent_clean = self.recent_clean
3144 self.recent_clean = True
3145 return recent_clean
3146
3147 def _MarkUnclean(self):
3148 self.clean = False
3149 self.recent_clean = False
3150
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003151 def _RunLater(self):
3152 for q in ['_later_queue1', '_later_queue2']:
3153 if not self._RunQueue(q):
3154 return
3155
3156 def _RunQueue(self, queue):
3157 for m in getattr(self, queue):
3158 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003159 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003160 return False
3161 setattr(self, queue, [])
3162 return True
3163
3164 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003165 if self._messages or self._failures:
3166 if os.isatty(2):
3167 self.out.write(progress.CSI_ERASE_LINE)
3168 self.out.write('\r')
3169
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003170 for m in self._messages:
3171 m.Print(self)
3172 for m in self._failures:
3173 m.Print(self)
3174
3175 self._messages = []
3176 self._failures = []
3177
3178
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003179class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003180
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003181 """A special project housed under .repo.
3182 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003183
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003184 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003185 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003186 manifest=manifest,
3187 name=name,
3188 gitdir=gitdir,
3189 objdir=gitdir,
3190 worktree=worktree,
3191 remote=RemoteSpec('origin'),
3192 relpath='.repo/%s' % name,
3193 revisionExpr='refs/heads/master',
3194 revisionId=None,
3195 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003196
3197 def PreSync(self):
3198 if self.Exists:
3199 cb = self.CurrentBranch
3200 if cb:
3201 base = self.GetBranch(cb).merge
3202 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003203 self.revisionExpr = base
3204 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003205
Martin Kelly224a31a2017-07-10 14:46:25 -07003206 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003207 """ Prepare MetaProject for manifest branch switch
3208 """
3209
3210 # detach and delete manifest branch, allowing a new
3211 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003212 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003213 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003214 syncbuf.Finish()
3215
3216 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003217 ['update-ref', '-d', 'refs/heads/default'],
3218 capture_stdout=True,
3219 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003220
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003221 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003222 def LastFetch(self):
3223 try:
3224 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3225 return os.path.getmtime(fh)
3226 except OSError:
3227 return 0
3228
3229 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003230 def HasChanges(self):
3231 """Has the remote received new commits not yet checked out?
3232 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003233 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003234 return False
3235
David Pursehouse8a68ff92012-09-24 12:15:13 +09003236 all_refs = self.bare_ref.all
3237 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003238 head = self.work_git.GetHead()
3239 if head.startswith(R_HEADS):
3240 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003241 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003242 except KeyError:
3243 head = None
3244
3245 if revid == head:
3246 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003247 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003248 return True
3249 return False