blob: cb78ff7231b81baa4592b8620b2fe9d19f08f45b [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
2#
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080018import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070020import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070022import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import re
24import shutil
25import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070026import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020028import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080029import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070030import time
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070031
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070033from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070034from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
35 ID_RE
Remy Bohmer16c13282020-09-10 10:38:04 +020036from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040037from error import ManifestInvalidRevisionError, ManifestInvalidPathError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040040import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040041from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050043from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044
David Pursehouse59bbb582013-05-17 10:49:33 +090045from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040046if is_python3():
47 import urllib.parse
48else:
49 import imp
50 import urlparse
51 urllib = imp.new_module('urllib')
52 urllib.parse = urlparse
David Pursehousea46bf7d2020-02-15 12:45:53 +090053 input = raw_input # noqa: F821
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
George Engelbrecht9bc283e2020-04-02 12:36:09 -060056# Maximum sleep time allowed during retries.
57MAXIMUM_RETRY_SLEEP_SEC = 3600.0
58# +-10% random jitter is added to each Fetches retry sleep duration.
59RETRY_JITTER_PERCENT = 0.1
60
61
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062def _lwrite(path, content):
63 lock = '%s.lock' % path
64
Remy Bohmer169b0212020-11-21 10:57:52 +010065 # Maintain Unix line endings on all OS's to match git behavior.
66 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068
69 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070070 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070071 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080072 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070073 raise
74
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
Shawn O. Pearce48244782009-04-16 08:25:57 -070076def _error(fmt, *args):
77 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070078 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070079
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
David Pursehousef33929d2015-08-24 14:39:14 +090081def _warn(fmt, *args):
82 msg = fmt % args
83 print('warn: %s' % msg, file=sys.stderr)
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070086def not_rev(r):
87 return '^' + r
88
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080090def sq(r):
91 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080092
David Pursehouse819827a2020-02-12 15:20:19 +090093
Jonathan Nieder93719792015-03-17 11:29:58 -070094_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070095
96
Jonathan Nieder93719792015-03-17 11:29:58 -070097def _ProjectHooks():
98 """List the hooks present in the 'hooks' directory.
99
100 These hooks are project hooks and are copied to the '.git/hooks' directory
101 of all subprojects.
102
103 This function caches the list of hooks (based on the contents of the
104 'repo/hooks' directory) on the first call.
105
106 Returns:
107 A list of absolute paths to all of the files in the hooks directory.
108 """
109 global _project_hook_list
110 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700111 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700112 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700113 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700114 return _project_hook_list
115
116
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700117class DownloadedChange(object):
118 _commit_cache = None
119
120 def __init__(self, project, base, change_id, ps_id, commit):
121 self.project = project
122 self.base = base
123 self.change_id = change_id
124 self.ps_id = ps_id
125 self.commit = commit
126
127 @property
128 def commits(self):
129 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700130 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
131 '--abbrev-commit',
132 '--pretty=oneline',
133 '--reverse',
134 '--date-order',
135 not_rev(self.base),
136 self.commit,
137 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700138 return self._commit_cache
139
140
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700141class ReviewableBranch(object):
142 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400143 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700144
145 def __init__(self, project, branch, base):
146 self.project = project
147 self.branch = branch
148 self.base = base
149
150 @property
151 def name(self):
152 return self.branch.name
153
154 @property
155 def commits(self):
156 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400157 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
158 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
159 try:
160 self._commit_cache = self.project.bare_git.rev_list(*args)
161 except GitError:
162 # We weren't able to probe the commits for this branch. Was it tracking
163 # a branch that no longer exists? If so, return no commits. Otherwise,
164 # rethrow the error as we don't know what's going on.
165 if self.base_exists:
166 raise
167
168 self._commit_cache = []
169
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 return self._commit_cache
171
172 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800173 def unabbrev_commits(self):
174 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700175 for commit in self.project.bare_git.rev_list(not_rev(self.base),
176 R_HEADS + self.name,
177 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800178 r[commit[0:8]] = commit
179 return r
180
181 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700183 return self.project.bare_git.log('--pretty=format:%cd',
184 '-n', '1',
185 R_HEADS + self.name,
186 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187
Mike Frysinger6da17752019-09-11 18:43:17 -0400188 @property
189 def base_exists(self):
190 """Whether the branch we're tracking exists.
191
192 Normally it should, but sometimes branches we track can get deleted.
193 """
194 if self._base_exists is None:
195 try:
196 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
197 # If we're still here, the base branch exists.
198 self._base_exists = True
199 except GitError:
200 # If we failed to verify, the base branch doesn't exist.
201 self._base_exists = False
202
203 return self._base_exists
204
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700205 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500206 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700207 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500208 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500209 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200210 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700211 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200213 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800214 validate_certs=True,
215 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500216 self.project.UploadForReview(branch=self.name,
217 people=people,
218 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700219 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500220 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500221 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200222 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700223 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200224 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200225 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800226 validate_certs=validate_certs,
227 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700228
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700229 def GetPublishedRefs(self):
230 refs = {}
231 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700232 self.branch.remote.SshReviewUrl(self.project.UserEmail),
233 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700234 for line in output.split('\n'):
235 try:
236 (sha, ref) = line.split()
237 refs[sha] = ref
238 except ValueError:
239 pass
240
241 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700243
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700245
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 def __init__(self, config):
247 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100248 self.project = self.printer('header', attr='bold')
249 self.branch = self.printer('header', attr='bold')
250 self.nobranch = self.printer('nobranch', fg='red')
251 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252
Anthony King7bdac712014-07-16 12:56:40 +0100253 self.added = self.printer('added', fg='green')
254 self.changed = self.printer('changed', fg='red')
255 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700256
257
258class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700259
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260 def __init__(self, config):
261 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100262 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400263 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700264
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700265
Anthony King7bdac712014-07-16 12:56:40 +0100266class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700267
James W. Mills24c13082012-04-12 15:04:13 -0500268 def __init__(self, name, value, keep):
269 self.name = name
270 self.value = value
271 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700273
Mike Frysingere6a202f2019-08-02 15:57:57 -0400274def _SafeExpandPath(base, subpath, skipfinal=False):
275 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700276
Mike Frysingere6a202f2019-08-02 15:57:57 -0400277 We make sure no intermediate symlinks are traversed, and that the final path
278 is not a special file (e.g. not a socket or fifo).
279
280 NB: We rely on a number of paths already being filtered out while parsing the
281 manifest. See the validation logic in manifest_xml.py for more details.
282 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500283 # Split up the path by its components. We can't use os.path.sep exclusively
284 # as some platforms (like Windows) will convert / to \ and that bypasses all
285 # our constructed logic here. Especially since manifest authors only use
286 # / in their paths.
287 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
288 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400289 if skipfinal:
290 # Whether the caller handles the final component itself.
291 finalpart = components.pop()
292
293 path = base
294 for part in components:
295 if part in {'.', '..'}:
296 raise ManifestInvalidPathError(
297 '%s: "%s" not allowed in paths' % (subpath, part))
298
299 path = os.path.join(path, part)
300 if platform_utils.islink(path):
301 raise ManifestInvalidPathError(
302 '%s: traversing symlinks not allow' % (path,))
303
304 if os.path.exists(path):
305 if not os.path.isfile(path) and not platform_utils.isdir(path):
306 raise ManifestInvalidPathError(
307 '%s: only regular files & directories allowed' % (path,))
308
309 if skipfinal:
310 path = os.path.join(path, finalpart)
311
312 return path
313
314
315class _CopyFile(object):
316 """Container for <copyfile> manifest element."""
317
318 def __init__(self, git_worktree, src, topdir, dest):
319 """Register a <copyfile> request.
320
321 Args:
322 git_worktree: Absolute path to the git project checkout.
323 src: Relative path under |git_worktree| of file to read.
324 topdir: Absolute path to the top of the repo client checkout.
325 dest: Relative path under |topdir| of file to write.
326 """
327 self.git_worktree = git_worktree
328 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700329 self.src = src
330 self.dest = dest
331
332 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400333 src = _SafeExpandPath(self.git_worktree, self.src)
334 dest = _SafeExpandPath(self.topdir, self.dest)
335
336 if platform_utils.isdir(src):
337 raise ManifestInvalidPathError(
338 '%s: copying from directory not supported' % (self.src,))
339 if platform_utils.isdir(dest):
340 raise ManifestInvalidPathError(
341 '%s: copying to directory not allowed' % (self.dest,))
342
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700343 # copy file if it does not exist or is out of date
344 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
345 try:
346 # remove existing file first, since it might be read-only
347 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800348 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400349 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200350 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700351 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200352 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700353 shutil.copy(src, dest)
354 # make the file read-only
355 mode = os.stat(dest)[stat.ST_MODE]
356 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
357 os.chmod(dest, mode)
358 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700359 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700360
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700361
Anthony King7bdac712014-07-16 12:56:40 +0100362class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400363 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700364
Mike Frysingere6a202f2019-08-02 15:57:57 -0400365 def __init__(self, git_worktree, src, topdir, dest):
366 """Register a <linkfile> request.
367
368 Args:
369 git_worktree: Absolute path to the git project checkout.
370 src: Target of symlink relative to path under |git_worktree|.
371 topdir: Absolute path to the top of the repo client checkout.
372 dest: Relative path under |topdir| of symlink to create.
373 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700374 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400375 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500376 self.src = src
377 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500378
Wink Saville4c426ef2015-06-03 08:05:17 -0700379 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500380 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700381 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500382 try:
383 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800384 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800385 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500386 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700387 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700388 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500389 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700390 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500391 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700392 _error('Cannot link file %s to %s', relSrc, absDest)
393
394 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400395 """Link the self.src & self.dest paths.
396
397 Handles wild cards on the src linking all of the files in the source in to
398 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700399 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500400 # Some people use src="." to create stable links to projects. Lets allow
401 # that but reject all other uses of "." to keep things simple.
402 if self.src == '.':
403 src = self.git_worktree
404 else:
405 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400406
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300407 if not glob.has_magic(src):
408 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400409 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
410 # dest & src are absolute paths at this point. Make sure the target of
411 # the symlink is relative in the context of the repo client checkout.
412 relpath = os.path.relpath(src, os.path.dirname(dest))
413 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700414 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400415 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300416 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400417 if os.path.exists(dest) and not platform_utils.isdir(dest):
418 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700419 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400420 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700421 # Create a releative path from source dir to destination dir
422 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400423 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700424
425 # Get the source file name
426 srcFile = os.path.basename(absSrcFile)
427
428 # Now form the final full paths to srcFile. They will be
429 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400430 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700431 relSrc = os.path.join(relSrcDir, srcFile)
432 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500433
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700434
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700435class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700436
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700437 def __init__(self,
438 name,
Anthony King7bdac712014-07-16 12:56:40 +0100439 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700440 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100441 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700442 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700443 orig_name=None,
444 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700445 self.name = name
446 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700447 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700448 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100449 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700450 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700451 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700452
453class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600454 # These objects can be shared between several working trees.
455 shareable_files = ['description', 'info']
456 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
457 # These objects can only be used by a single working tree.
458 working_tree_files = ['config', 'packed-refs', 'shallow']
459 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700460
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700461 def __init__(self,
462 manifest,
463 name,
464 remote,
465 gitdir,
David James8d201162013-10-11 17:03:19 -0700466 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700467 worktree,
468 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700469 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800470 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100471 rebase=True,
472 groups=None,
473 sync_c=False,
474 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900475 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100476 clone_depth=None,
477 upstream=None,
478 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500479 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100480 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900481 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700482 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600483 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700484 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800485 """Init a Project object.
486
487 Args:
488 manifest: The XmlManifest object.
489 name: The `name` attribute of manifest.xml's project element.
490 remote: RemoteSpec object specifying its remote's properties.
491 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700492 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800493 worktree: Absolute path of git working tree.
494 relpath: Relative path of git working tree to repo's top directory.
495 revisionExpr: The `revision` attribute of manifest.xml's project element.
496 revisionId: git commit id for checking out.
497 rebase: The `rebase` attribute of manifest.xml's project element.
498 groups: The `groups` attribute of manifest.xml's project element.
499 sync_c: The `sync-c` attribute of manifest.xml's project element.
500 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900501 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800502 upstream: The `upstream` attribute of manifest.xml's project element.
503 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500504 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800505 is_derived: False if the project was explicitly defined in the manifest;
506 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400507 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900508 optimized_fetch: If True, when a project is set to a sha1 revision, only
509 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600510 retry_fetches: Retry remote fetches n times upon receiving transient error
511 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700512 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800513 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400514 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700515 self.name = name
516 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800517 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700518 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800519 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700520 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800521 else:
522 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700523 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700524 self.revisionExpr = revisionExpr
525
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700526 if revisionId is None \
527 and revisionExpr \
528 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700529 self.revisionId = revisionExpr
530 else:
531 self.revisionId = revisionId
532
Mike Pontillod3153822012-02-28 11:53:24 -0800533 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700534 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700535 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800536 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900537 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900538 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700539 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800540 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500541 # NB: Do not use this setting in __init__ to change behavior so that the
542 # manifest.git checkout can inspect & change it after instantiating. See
543 # the XmlManifest init code for more info.
544 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800545 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900546 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600547 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800548 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800549
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700550 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700551 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500552 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500553 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700554 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400555 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700556
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800557 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700558 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800559 else:
560 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700561 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700562 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700563 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400564 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700565 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700566
Doug Anderson37282b42011-03-04 11:54:18 -0800567 # This will be filled in if a project is later identified to be the
568 # project containing repo hooks.
569 self.enabled_repo_hooks = []
570
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700571 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800572 def Derived(self):
573 return self.is_derived
574
575 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700576 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700577 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700578
579 @property
580 def CurrentBranch(self):
581 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400582
583 The branch name omits the 'refs/heads/' prefix.
584 None is returned if the project is on a detached HEAD, or if the work_git is
585 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700586 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400587 try:
588 b = self.work_git.GetHead()
589 except NoManifestException:
590 # If the local checkout is in a bad state, don't barf. Let the callers
591 # process this like the head is unreadable.
592 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700593 if b.startswith(R_HEADS):
594 return b[len(R_HEADS):]
595 return None
596
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700597 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500598 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
599 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
600 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200601
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700602 def IsDirty(self, consider_untracked=True):
603 """Is the working directory modified in some way?
604 """
605 self.work_git.update_index('-q',
606 '--unmerged',
607 '--ignore-missing',
608 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900609 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700610 return True
611 if self.work_git.DiffZ('diff-files'):
612 return True
613 if consider_untracked and self.work_git.LsOthers():
614 return True
615 return False
616
617 _userident_name = None
618 _userident_email = None
619
620 @property
621 def UserName(self):
622 """Obtain the user's personal name.
623 """
624 if self._userident_name is None:
625 self._LoadUserIdentity()
626 return self._userident_name
627
628 @property
629 def UserEmail(self):
630 """Obtain the user's email address. This is very likely
631 to be their Gerrit login.
632 """
633 if self._userident_email is None:
634 self._LoadUserIdentity()
635 return self._userident_email
636
637 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900638 u = self.bare_git.var('GIT_COMMITTER_IDENT')
639 m = re.compile("^(.*) <([^>]*)> ").match(u)
640 if m:
641 self._userident_name = m.group(1)
642 self._userident_email = m.group(2)
643 else:
644 self._userident_name = ''
645 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700646
647 def GetRemote(self, name):
648 """Get the configuration for a single remote.
649 """
650 return self.config.GetRemote(name)
651
652 def GetBranch(self, name):
653 """Get the configuration for a single branch.
654 """
655 return self.config.GetBranch(name)
656
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700657 def GetBranches(self):
658 """Get all existing local branches.
659 """
660 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900661 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700662 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700663
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530664 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700665 if name.startswith(R_HEADS):
666 name = name[len(R_HEADS):]
667 b = self.GetBranch(name)
668 b.current = name == current
669 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900670 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700671 heads[name] = b
672
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530673 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700674 if name.startswith(R_PUB):
675 name = name[len(R_PUB):]
676 b = heads.get(name)
677 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900678 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700679
680 return heads
681
Colin Cross5acde752012-03-28 20:15:45 -0700682 def MatchesGroups(self, manifest_groups):
683 """Returns true if the manifest groups specified at init should cause
684 this project to be synced.
685 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700686 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700687
688 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700689 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700690 manifest_groups: "-group1,group2"
691 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500692
693 The special manifest group "default" will match any project that
694 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700695 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500696 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700697 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700698 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500699 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700700
Conley Owens971de8e2012-04-16 10:36:08 -0700701 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700702 for group in expanded_manifest_groups:
703 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700704 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700705 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700706 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700707
Conley Owens971de8e2012-04-16 10:36:08 -0700708 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700709
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700710# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700711 def UncommitedFiles(self, get_all=True):
712 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700713
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700714 Args:
715 get_all: a boolean, if True - get information about all different
716 uncommitted files. If False - return as soon as any kind of
717 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500718 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700719 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500720 self.work_git.update_index('-q',
721 '--unmerged',
722 '--ignore-missing',
723 '--refresh')
724 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700725 details.append("rebase in progress")
726 if not get_all:
727 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500728
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700729 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
730 if changes:
731 details.extend(changes)
732 if not get_all:
733 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500734
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700735 changes = self.work_git.DiffZ('diff-files').keys()
736 if changes:
737 details.extend(changes)
738 if not get_all:
739 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500740
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700741 changes = self.work_git.LsOthers()
742 if changes:
743 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500744
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700745 return details
746
747 def HasChanges(self):
748 """Returns true if there are uncommitted changes.
749 """
750 if self.UncommitedFiles(get_all=False):
751 return True
752 else:
753 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500754
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600755 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200757
758 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200759 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600760 quiet: If True then only print the project name. Do not print
761 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700763 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700764 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200765 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700766 print(file=output_redir)
767 print('project %s/' % self.relpath, file=output_redir)
768 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700769 return
770
771 self.work_git.update_index('-q',
772 '--unmerged',
773 '--ignore-missing',
774 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700775 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
777 df = self.work_git.DiffZ('diff-files')
778 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100779 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700780 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700781
782 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700783 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200784 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700785 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700786
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600787 if quiet:
788 out.nl()
789 return 'DIRTY'
790
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791 branch = self.CurrentBranch
792 if branch is None:
793 out.nobranch('(*** NO BRANCH ***)')
794 else:
795 out.branch('branch %s', branch)
796 out.nl()
797
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700798 if rb:
799 out.important('prior sync failed; rebase still in progress')
800 out.nl()
801
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700802 paths = list()
803 paths.extend(di.keys())
804 paths.extend(df.keys())
805 paths.extend(do)
806
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530807 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900808 try:
809 i = di[p]
810 except KeyError:
811 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900813 try:
814 f = df[p]
815 except KeyError:
816 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200817
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900818 if i:
819 i_status = i.status.upper()
820 else:
821 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900823 if f:
824 f_status = f.status.lower()
825 else:
826 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700827
828 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800829 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700830 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831 else:
832 line = ' %s%s\t%s' % (i_status, f_status, p)
833
834 if i and not f:
835 out.added('%s', line)
836 elif (i and f) or (not i and f):
837 out.changed('%s', line)
838 elif not i and not f:
839 out.untracked('%s', line)
840 else:
841 out.write('%s', line)
842 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200843
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700844 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700845
pelyad67872d2012-03-28 14:49:58 +0300846 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700847 """Prints the status of the repository to stdout.
848 """
849 out = DiffColoring(self.config)
850 cmd = ['diff']
851 if out.is_on:
852 cmd.append('--color')
853 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300854 if absolute_paths:
855 cmd.append('--src-prefix=a/%s/' % self.relpath)
856 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400858 try:
859 p = GitCommand(self,
860 cmd,
861 capture_stdout=True,
862 capture_stderr=True)
863 except GitError as e:
864 out.nl()
865 out.project('project %s/' % self.relpath)
866 out.nl()
867 out.fail('%s', str(e))
868 out.nl()
869 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700870 has_diff = False
871 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -0400872 if not hasattr(line, 'encode'):
873 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700874 if not has_diff:
875 out.nl()
876 out.project('project %s/' % self.relpath)
877 out.nl()
878 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -0700879 print(line[:-1])
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400880 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700881
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700882# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900883 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884 """Was the branch published (uploaded) for code review?
885 If so, returns the SHA-1 hash of the last published
886 state for the branch.
887 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700888 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900889 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700890 try:
891 return self.bare_git.rev_parse(key)
892 except GitError:
893 return None
894 else:
895 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900896 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700897 except KeyError:
898 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700899
David Pursehouse8a68ff92012-09-24 12:15:13 +0900900 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 """Prunes any stale published refs.
902 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900903 if all_refs is None:
904 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700905 heads = set()
906 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530907 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700908 if name.startswith(R_HEADS):
909 heads.add(name)
910 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900911 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700912
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530913 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914 n = name[len(R_PUB):]
915 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900916 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700917
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700918 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700919 """List any branches which can be uploaded for review.
920 """
921 heads = {}
922 pubed = {}
923
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530924 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700925 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900926 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900928 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929
930 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530931 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900932 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700934 if selected_branch and branch != selected_branch:
935 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800937 rb = self.GetUploadableBranch(branch)
938 if rb:
939 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700940 return ready
941
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800942 def GetUploadableBranch(self, branch_name):
943 """Get a single uploadable branch, or None.
944 """
945 branch = self.GetBranch(branch_name)
946 base = branch.LocalMerge
947 if branch.LocalMerge:
948 rb = ReviewableBranch(self, branch, base)
949 if rb.commits:
950 return rb
951 return None
952
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700953 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100954 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500955 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700956 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500957 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500958 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200959 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700960 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200961 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200962 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800963 validate_certs=True,
964 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700965 """Uploads the named branch for code review.
966 """
967 if branch is None:
968 branch = self.CurrentBranch
969 if branch is None:
970 raise GitError('not currently on a branch')
971
972 branch = self.GetBranch(branch)
973 if not branch.LocalMerge:
974 raise GitError('branch %s does not track a remote' % branch.name)
975 if not branch.remote.review:
976 raise GitError('remote %s has no review url' % branch.remote.name)
977
Bryan Jacobsf609f912013-05-06 13:36:24 -0400978 if dest_branch is None:
979 dest_branch = self.dest_branch
980 if dest_branch is None:
981 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982 if not dest_branch.startswith(R_HEADS):
983 dest_branch = R_HEADS + dest_branch
984
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800985 if not branch.remote.projectname:
986 branch.remote.projectname = self.name
987 branch.remote.Save()
988
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200989 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800990 if url is None:
991 raise UploadError('review not configured')
992 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500993 if dryrun:
994 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800995
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800996 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -0800997 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700998
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800999 for push_option in (push_options or []):
1000 cmd.append('-o')
1001 cmd.append(push_option)
1002
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001003 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001004
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001005 if dest_branch.startswith(R_HEADS):
1006 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001007
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001008 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001009 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001010 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001011 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001012 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001013 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001014
David Pursehousef25a3702018-11-14 19:01:22 -08001015 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001016 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001017 if notify:
1018 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001019 if private:
1020 opts += ['private']
1021 if wip:
1022 opts += ['wip']
1023 if opts:
1024 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001025 cmd.append(ref_spec)
1026
Anthony King7bdac712014-07-16 12:56:40 +01001027 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001028 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029
Mike Frysingerd7f86832020-11-19 19:18:46 -05001030 if not dryrun:
1031 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1032 self.bare_git.UpdateRef(R_PUB + branch.name,
1033 R_HEADS + branch.name,
1034 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001036# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001037 def _ExtractArchive(self, tarpath, path=None):
1038 """Extract the given tar on its current location
1039
1040 Args:
1041 - tarpath: The path to the actual tar file
1042
1043 """
1044 try:
1045 with tarfile.open(tarpath, 'r') as tar:
1046 tar.extractall(path=path)
1047 return True
1048 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001049 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001050 return False
1051
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001052 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001053 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001054 verbose=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001055 is_new=None,
1056 current_branch_only=False,
1057 force_sync=False,
1058 clone_bundle=True,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001059 tags=True,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001060 archive=False,
1061 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001062 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001063 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001064 submodules=False,
1065 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 """Perform only the network IO portion of the sync process.
1067 Local working directory/branch state is not affected.
1068 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001069 if archive and not isinstance(self, MetaProject):
1070 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001071 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001072 return False
1073
1074 name = self.relpath.replace('\\', '/')
1075 name = name.replace('/', '_')
1076 tarpath = '%s.tar' % name
1077 topdir = self.manifest.topdir
1078
1079 try:
1080 self._FetchArchive(tarpath, cwd=topdir)
1081 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001082 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001083 return False
1084
1085 # From now on, we only need absolute tarpath
1086 tarpath = os.path.join(topdir, tarpath)
1087
1088 if not self._ExtractArchive(tarpath, path=topdir):
1089 return False
1090 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001091 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001092 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001093 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001094 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001095 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001096 if is_new is None:
1097 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001098 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001099 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001100 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001101 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001103
1104 if is_new:
1105 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1106 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001107 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001108 # This works for both absolute and relative alternate directories.
1109 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001110 except IOError:
1111 alt_dir = None
1112 else:
1113 alt_dir = None
1114
Mike Frysingere50b6a72020-02-19 01:45:48 -05001115 if (clone_bundle
1116 and alt_dir is None
1117 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001118 is_new = False
1119
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001120 if not current_branch_only:
1121 if self.sync_c:
1122 current_branch_only = True
1123 elif not self.manifest._loaded:
1124 # Manifest cannot check defaults until it syncs.
1125 current_branch_only = False
1126 elif self.manifest.default.sync_c:
1127 current_branch_only = True
1128
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001129 if not self.sync_tags:
1130 tags = False
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001131
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001132 if self.clone_depth:
1133 depth = self.clone_depth
1134 else:
1135 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1136
Mike Frysinger521d01b2020-02-17 01:51:49 -05001137 # See if we can skip the network fetch entirely.
1138 if not (optimized_fetch and
1139 (ID_RE.match(self.revisionExpr) and
1140 self._CheckForImmutableRevision())):
1141 if not self._RemoteFetch(
David Pursehouse3cceda52020-02-18 14:11:39 +09001142 initial=is_new, quiet=quiet, verbose=verbose, alt_dir=alt_dir,
1143 current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001144 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001145 submodules=submodules, force_sync=force_sync,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001146 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001147 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001148
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001149 mp = self.manifest.manifestProject
1150 dissociate = mp.config.GetBoolean('repo.dissociate')
1151 if dissociate:
1152 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1153 if os.path.exists(alternates_file):
1154 cmd = ['repack', '-a', '-d']
1155 if GitCommand(self, cmd, bare=True).Wait() != 0:
1156 return False
1157 platform_utils.remove(alternates_file)
1158
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001159 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001160 self._InitMRef()
1161 else:
1162 self._InitMirrorHead()
1163 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001164 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001165 except OSError:
1166 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001167 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001168
1169 def PostRepoUpgrade(self):
1170 self._InitHooks()
1171
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001172 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001173 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001174 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001175 for copyfile in self.copyfiles:
1176 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001177 for linkfile in self.linkfiles:
1178 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001179
Julien Camperguedd654222014-01-09 16:21:37 +01001180 def GetCommitRevisionId(self):
1181 """Get revisionId of a commit.
1182
1183 Use this method instead of GetRevisionId to get the id of the commit rather
1184 than the id of the current git object (for example, a tag)
1185
1186 """
1187 if not self.revisionExpr.startswith(R_TAGS):
1188 return self.GetRevisionId(self._allrefs)
1189
1190 try:
1191 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1192 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001193 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1194 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001195
David Pursehouse8a68ff92012-09-24 12:15:13 +09001196 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001197 if self.revisionId:
1198 return self.revisionId
1199
1200 rem = self.GetRemote(self.remote.name)
1201 rev = rem.ToLocal(self.revisionExpr)
1202
David Pursehouse8a68ff92012-09-24 12:15:13 +09001203 if all_refs is not None and rev in all_refs:
1204 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001205
1206 try:
1207 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1208 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001209 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1210 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001211
Martin Kellye4e94d22017-03-21 16:05:12 -07001212 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001213 """Perform only the local IO portion of the sync process.
1214 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001215 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001216 if not os.path.exists(self.gitdir):
1217 syncbuf.fail(self,
1218 'Cannot checkout %s due to missing network sync; Run '
1219 '`repo sync -n %s` first.' %
1220 (self.name, self.name))
1221 return
1222
Martin Kellye4e94d22017-03-21 16:05:12 -07001223 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001224 all_refs = self.bare_ref.all
1225 self.CleanPublishedCache(all_refs)
1226 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001227
David Pursehouse1d947b32012-10-25 12:23:11 +09001228 def _doff():
1229 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001230 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001231
Martin Kellye4e94d22017-03-21 16:05:12 -07001232 def _dosubmodules():
1233 self._SyncSubmodules(quiet=True)
1234
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001235 head = self.work_git.GetHead()
1236 if head.startswith(R_HEADS):
1237 branch = head[len(R_HEADS):]
1238 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001239 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001240 except KeyError:
1241 head = None
1242 else:
1243 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001244
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001245 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001246 # Currently on a detached HEAD. The user is assumed to
1247 # not have any local modifications worth worrying about.
1248 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001249 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001250 syncbuf.fail(self, _PriorSyncFailedError())
1251 return
1252
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001253 if head == revid:
1254 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001255 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001256 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001257 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001258 # The copy/linkfile config may have changed.
1259 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001260 return
1261 else:
1262 lost = self._revlist(not_rev(revid), HEAD)
1263 if lost:
1264 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001265
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001267 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001268 if submodules:
1269 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001270 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001271 syncbuf.fail(self, e)
1272 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001273 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001274 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001275
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001276 if head == revid:
1277 # No changes; don't do anything further.
1278 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001279 # The copy/linkfile config may have changed.
1280 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001281 return
1282
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001283 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001284
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001285 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001286 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001287 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001288 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001289 syncbuf.info(self,
1290 "leaving %s; does not track upstream",
1291 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001292 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001293 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001294 if submodules:
1295 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001296 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001297 syncbuf.fail(self, e)
1298 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001299 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001300 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001301
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001302 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001303
1304 # See if we can perform a fast forward merge. This can happen if our
1305 # branch isn't in the exact same state as we last published.
1306 try:
1307 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1308 # Skip the published logic.
1309 pub = False
1310 except GitError:
1311 pub = self.WasPublished(branch.name, all_refs)
1312
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001313 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001314 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001315 if not_merged:
1316 if upstream_gain:
1317 # The user has published this branch and some of those
1318 # commits are not yet merged upstream. We do not want
1319 # to rewrite the published commits so we punt.
1320 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001321 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001322 "branch %s is published (but not merged) and is now "
1323 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001324 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001325 elif pub == head:
1326 # All published commits are merged, and thus we are a
1327 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001328 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001329 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001330 if submodules:
1331 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001332 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001333
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001334 # Examine the local commits not in the remote. Find the
1335 # last one attributed to this user, if any.
1336 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001337 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001338 last_mine = None
1339 cnt_mine = 0
1340 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001341 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001342 if committer_email == self.UserEmail:
1343 last_mine = commit_id
1344 cnt_mine += 1
1345
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001346 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001347 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001348
1349 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001350 syncbuf.fail(self, _DirtyError())
1351 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001352
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001353 # If the upstream switched on us, warn the user.
1354 #
1355 if branch.merge != self.revisionExpr:
1356 if branch.merge and self.revisionExpr:
1357 syncbuf.info(self,
1358 'manifest switched %s...%s',
1359 branch.merge,
1360 self.revisionExpr)
1361 elif branch.merge:
1362 syncbuf.info(self,
1363 'manifest no longer tracks %s',
1364 branch.merge)
1365
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001366 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001367 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001368 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001369 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001370 syncbuf.info(self,
1371 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001372 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001373
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001374 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001375 if not ID_RE.match(self.revisionExpr):
1376 # in case of manifest sync the revisionExpr might be a SHA1
1377 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001378 if not branch.merge.startswith('refs/'):
1379 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001380 branch.Save()
1381
Mike Pontillod3153822012-02-28 11:53:24 -08001382 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001383 def _docopyandlink():
1384 self._CopyAndLinkFiles()
1385
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001386 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001387 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001388 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001389 if submodules:
1390 syncbuf.later2(self, _dosubmodules)
1391 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001392 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001393 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001394 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001395 if submodules:
1396 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001397 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001398 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001399 syncbuf.fail(self, e)
1400 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001401 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001402 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001403 if submodules:
1404 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405
Mike Frysingere6a202f2019-08-02 15:57:57 -04001406 def AddCopyFile(self, src, dest, topdir):
1407 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001408
Mike Frysingere6a202f2019-08-02 15:57:57 -04001409 No filesystem changes occur here. Actual copying happens later on.
1410
1411 Paths should have basic validation run on them before being queued.
1412 Further checking will be handled when the actual copy happens.
1413 """
1414 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1415
1416 def AddLinkFile(self, src, dest, topdir):
1417 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1418
1419 No filesystem changes occur here. Actual linking happens later on.
1420
1421 Paths should have basic validation run on them before being queued.
1422 Further checking will be handled when the actual link happens.
1423 """
1424 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001425
James W. Mills24c13082012-04-12 15:04:13 -05001426 def AddAnnotation(self, name, value, keep):
1427 self.annotations.append(_Annotation(name, value, keep))
1428
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001429 def DownloadPatchSet(self, change_id, patch_id):
1430 """Download a single patch set of a single change to FETCH_HEAD.
1431 """
1432 remote = self.GetRemote(self.remote.name)
1433
1434 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001435 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001436 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001437 if GitCommand(self, cmd, bare=True).Wait() != 0:
1438 return None
1439 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001440 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001441 change_id,
1442 patch_id,
1443 self.bare_git.rev_parse('FETCH_HEAD'))
1444
Mike Frysingerc0d18662020-02-19 19:19:18 -05001445 def DeleteWorktree(self, quiet=False, force=False):
1446 """Delete the source checkout and any other housekeeping tasks.
1447
1448 This currently leaves behind the internal .repo/ cache state. This helps
1449 when switching branches or manifest changes get reverted as we don't have
1450 to redownload all the git objects. But we should do some GC at some point.
1451
1452 Args:
1453 quiet: Whether to hide normal messages.
1454 force: Always delete tree even if dirty.
1455
1456 Returns:
1457 True if the worktree was completely cleaned out.
1458 """
1459 if self.IsDirty():
1460 if force:
1461 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1462 (self.relpath,), file=sys.stderr)
1463 else:
1464 print('error: %s: Cannot remove project: uncommitted changes are '
1465 'present.\n' % (self.relpath,), file=sys.stderr)
1466 return False
1467
1468 if not quiet:
1469 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1470
1471 # Unlock and delink from the main worktree. We don't use git's worktree
1472 # remove because it will recursively delete projects -- we handle that
1473 # ourselves below. https://crbug.com/git/48
1474 if self.use_git_worktrees:
1475 needle = platform_utils.realpath(self.gitdir)
1476 # Find the git worktree commondir under .repo/worktrees/.
1477 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1478 assert output.startswith('worktree '), output
1479 commondir = output[9:]
1480 # Walk each of the git worktrees to see where they point.
1481 configs = os.path.join(commondir, 'worktrees')
1482 for name in os.listdir(configs):
1483 gitdir = os.path.join(configs, name, 'gitdir')
1484 with open(gitdir) as fp:
1485 relpath = fp.read().strip()
1486 # Resolve the checkout path and see if it matches this project.
1487 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1488 if fullpath == needle:
1489 platform_utils.rmtree(os.path.join(configs, name))
1490
1491 # Delete the .git directory first, so we're less likely to have a partially
1492 # working git repository around. There shouldn't be any git projects here,
1493 # so rmtree works.
1494
1495 # Try to remove plain files first in case of git worktrees. If this fails
1496 # for any reason, we'll fall back to rmtree, and that'll display errors if
1497 # it can't remove things either.
1498 try:
1499 platform_utils.remove(self.gitdir)
1500 except OSError:
1501 pass
1502 try:
1503 platform_utils.rmtree(self.gitdir)
1504 except OSError as e:
1505 if e.errno != errno.ENOENT:
1506 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1507 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1508 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1509 return False
1510
1511 # Delete everything under the worktree, except for directories that contain
1512 # another git project.
1513 dirs_to_remove = []
1514 failed = False
1515 for root, dirs, files in platform_utils.walk(self.worktree):
1516 for f in files:
1517 path = os.path.join(root, f)
1518 try:
1519 platform_utils.remove(path)
1520 except OSError as e:
1521 if e.errno != errno.ENOENT:
1522 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1523 failed = True
1524 dirs[:] = [d for d in dirs
1525 if not os.path.lexists(os.path.join(root, d, '.git'))]
1526 dirs_to_remove += [os.path.join(root, d) for d in dirs
1527 if os.path.join(root, d) not in dirs_to_remove]
1528 for d in reversed(dirs_to_remove):
1529 if platform_utils.islink(d):
1530 try:
1531 platform_utils.remove(d)
1532 except OSError as e:
1533 if e.errno != errno.ENOENT:
1534 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1535 failed = True
1536 elif not platform_utils.listdir(d):
1537 try:
1538 platform_utils.rmdir(d)
1539 except OSError as e:
1540 if e.errno != errno.ENOENT:
1541 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1542 failed = True
1543 if failed:
1544 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1545 file=sys.stderr)
1546 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1547 return False
1548
1549 # Try deleting parent dirs if they are empty.
1550 path = self.worktree
1551 while path != self.manifest.topdir:
1552 try:
1553 platform_utils.rmdir(path)
1554 except OSError as e:
1555 if e.errno != errno.ENOENT:
1556 break
1557 path = os.path.dirname(path)
1558
1559 return True
1560
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001561# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001562 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001563 """Create a new branch off the manifest's revision.
1564 """
Simran Basib9a1b732015-08-20 12:19:28 -07001565 if not branch_merge:
1566 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001567 head = self.work_git.GetHead()
1568 if head == (R_HEADS + name):
1569 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001570
David Pursehouse8a68ff92012-09-24 12:15:13 +09001571 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001572 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001573 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001574 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001575 capture_stdout=True,
1576 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001577
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001578 branch = self.GetBranch(name)
1579 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001580 branch.merge = branch_merge
1581 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1582 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001583
1584 if revision is None:
1585 revid = self.GetRevisionId(all_refs)
1586 else:
1587 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001588
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001589 if head.startswith(R_HEADS):
1590 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001591 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001592 except KeyError:
1593 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001594 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001595 ref = R_HEADS + name
1596 self.work_git.update_ref(ref, revid)
1597 self.work_git.symbolic_ref(HEAD, ref)
1598 branch.Save()
1599 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001600
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001601 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001602 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001603 capture_stdout=True,
1604 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001605 branch.Save()
1606 return True
1607 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001608
Wink Saville02d79452009-04-10 13:01:24 -07001609 def CheckoutBranch(self, name):
1610 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001611
1612 Args:
1613 name: The name of the branch to checkout.
1614
1615 Returns:
1616 True if the checkout succeeded; False if it didn't; None if the branch
1617 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001618 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001619 rev = R_HEADS + name
1620 head = self.work_git.GetHead()
1621 if head == rev:
1622 # Already on the branch
1623 #
1624 return True
Wink Saville02d79452009-04-10 13:01:24 -07001625
David Pursehouse8a68ff92012-09-24 12:15:13 +09001626 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001627 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001628 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001629 except KeyError:
1630 # Branch does not exist in this project
1631 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001632 return None
Wink Saville02d79452009-04-10 13:01:24 -07001633
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001634 if head.startswith(R_HEADS):
1635 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001636 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001637 except KeyError:
1638 head = None
1639
1640 if head == revid:
1641 # Same revision; just update HEAD to point to the new
1642 # target branch, but otherwise take no other action.
1643 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001644 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1645 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001646 return True
1647
1648 return GitCommand(self,
1649 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001650 capture_stdout=True,
1651 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001652
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001653 def AbandonBranch(self, name):
1654 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001655
1656 Args:
1657 name: The name of the branch to abandon.
1658
1659 Returns:
1660 True if the abandon succeeded; False if it didn't; None if the branch
1661 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001662 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001663 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001664 all_refs = self.bare_ref.all
1665 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001666 # Doesn't exist
1667 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001668
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001669 head = self.work_git.GetHead()
1670 if head == rev:
1671 # We can't destroy the branch while we are sitting
1672 # on it. Switch to a detached HEAD.
1673 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001674 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001675
David Pursehouse8a68ff92012-09-24 12:15:13 +09001676 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001677 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001678 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001679 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001680 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001681
1682 return GitCommand(self,
1683 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001684 capture_stdout=True,
1685 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001686
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001687 def PruneHeads(self):
1688 """Prune any topic branches already merged into upstream.
1689 """
1690 cb = self.CurrentBranch
1691 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001692 left = self._allrefs
1693 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001694 if name.startswith(R_HEADS):
1695 name = name[len(R_HEADS):]
1696 if cb is None or name != cb:
1697 kill.append(name)
1698
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001699 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001700 if cb is not None \
1701 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001702 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001703 self.work_git.DetachHead(HEAD)
1704 kill.append(cb)
1705
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001706 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001707 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001708
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001709 try:
1710 self.bare_git.DetachHead(rev)
1711
1712 b = ['branch', '-d']
1713 b.extend(kill)
1714 b = GitCommand(self, b, bare=True,
1715 capture_stdout=True,
1716 capture_stderr=True)
1717 b.Wait()
1718 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001719 if ID_RE.match(old):
1720 self.bare_git.DetachHead(old)
1721 else:
1722 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001723 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001724
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001725 for branch in kill:
1726 if (R_HEADS + branch) not in left:
1727 self.CleanPublishedCache()
1728 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729
1730 if cb and cb not in kill:
1731 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001732 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001733
1734 kept = []
1735 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001736 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001737 branch = self.GetBranch(branch)
1738 base = branch.LocalMerge
1739 if not base:
1740 base = rev
1741 kept.append(ReviewableBranch(self, branch, base))
1742 return kept
1743
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001744# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001745 def GetRegisteredSubprojects(self):
1746 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001747
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001748 def rec(subprojects):
1749 if not subprojects:
1750 return
1751 result.extend(subprojects)
1752 for p in subprojects:
1753 rec(p.subprojects)
1754 rec(self.subprojects)
1755 return result
1756
1757 def _GetSubmodules(self):
1758 # Unfortunately we cannot call `git submodule status --recursive` here
1759 # because the working tree might not exist yet, and it cannot be used
1760 # without a working tree in its current implementation.
1761
1762 def get_submodules(gitdir, rev):
1763 # Parse .gitmodules for submodule sub_paths and sub_urls
1764 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1765 if not sub_paths:
1766 return []
1767 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1768 # revision of submodule repository
1769 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1770 submodules = []
1771 for sub_path, sub_url in zip(sub_paths, sub_urls):
1772 try:
1773 sub_rev = sub_revs[sub_path]
1774 except KeyError:
1775 # Ignore non-exist submodules
1776 continue
1777 submodules.append((sub_rev, sub_path, sub_url))
1778 return submodules
1779
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001780 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1781 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001782
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001783 def parse_gitmodules(gitdir, rev):
1784 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1785 try:
Anthony King7bdac712014-07-16 12:56:40 +01001786 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1787 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001788 except GitError:
1789 return [], []
1790 if p.Wait() != 0:
1791 return [], []
1792
1793 gitmodules_lines = []
1794 fd, temp_gitmodules_path = tempfile.mkstemp()
1795 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001796 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001797 os.close(fd)
1798 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001799 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1800 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001801 if p.Wait() != 0:
1802 return [], []
1803 gitmodules_lines = p.stdout.split('\n')
1804 except GitError:
1805 return [], []
1806 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001807 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001808
1809 names = set()
1810 paths = {}
1811 urls = {}
1812 for line in gitmodules_lines:
1813 if not line:
1814 continue
1815 m = re_path.match(line)
1816 if m:
1817 names.add(m.group(1))
1818 paths[m.group(1)] = m.group(2)
1819 continue
1820 m = re_url.match(line)
1821 if m:
1822 names.add(m.group(1))
1823 urls[m.group(1)] = m.group(2)
1824 continue
1825 names = sorted(names)
1826 return ([paths.get(name, '') for name in names],
1827 [urls.get(name, '') for name in names])
1828
1829 def git_ls_tree(gitdir, rev, paths):
1830 cmd = ['ls-tree', rev, '--']
1831 cmd.extend(paths)
1832 try:
Anthony King7bdac712014-07-16 12:56:40 +01001833 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1834 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001835 except GitError:
1836 return []
1837 if p.Wait() != 0:
1838 return []
1839 objects = {}
1840 for line in p.stdout.split('\n'):
1841 if not line.strip():
1842 continue
1843 object_rev, object_path = line.split()[2:4]
1844 objects[object_path] = object_rev
1845 return objects
1846
1847 try:
1848 rev = self.GetRevisionId()
1849 except GitError:
1850 return []
1851 return get_submodules(self.gitdir, rev)
1852
1853 def GetDerivedSubprojects(self):
1854 result = []
1855 if not self.Exists:
1856 # If git repo does not exist yet, querying its submodules will
1857 # mess up its states; so return here.
1858 return result
1859 for rev, path, url in self._GetSubmodules():
1860 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001861 relpath, worktree, gitdir, objdir = \
1862 self.manifest.GetSubprojectPaths(self, name, path)
1863 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001864 if project:
1865 result.extend(project.GetDerivedSubprojects())
1866 continue
David James8d201162013-10-11 17:03:19 -07001867
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001868 if url.startswith('..'):
1869 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001870 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001871 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001872 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001873 review=self.remote.review,
1874 revision=self.remote.revision)
1875 subproject = Project(manifest=self.manifest,
1876 name=name,
1877 remote=remote,
1878 gitdir=gitdir,
1879 objdir=objdir,
1880 worktree=worktree,
1881 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001882 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001883 revisionId=rev,
1884 rebase=self.rebase,
1885 groups=self.groups,
1886 sync_c=self.sync_c,
1887 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001888 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001889 parent=self,
1890 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001891 result.append(subproject)
1892 result.extend(subproject.GetDerivedSubprojects())
1893 return result
1894
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001895# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001896 def EnableRepositoryExtension(self, key, value='true', version=1):
1897 """Enable git repository extension |key| with |value|.
1898
1899 Args:
1900 key: The extension to enabled. Omit the "extensions." prefix.
1901 value: The value to use for the extension.
1902 version: The minimum git repository version needed.
1903 """
1904 # Make sure the git repo version is new enough already.
1905 found_version = self.config.GetInt('core.repositoryFormatVersion')
1906 if found_version is None:
1907 found_version = 0
1908 if found_version < version:
1909 self.config.SetString('core.repositoryFormatVersion', str(version))
1910
1911 # Enable the extension!
1912 self.config.SetString('extensions.%s' % (key,), value)
1913
Mike Frysinger50a81de2020-09-06 15:51:21 -04001914 def ResolveRemoteHead(self, name=None):
1915 """Find out what the default branch (HEAD) points to.
1916
1917 Normally this points to refs/heads/master, but projects are moving to main.
1918 Support whatever the server uses rather than hardcoding "master" ourselves.
1919 """
1920 if name is None:
1921 name = self.remote.name
1922
1923 # The output will look like (NB: tabs are separators):
1924 # ref: refs/heads/master HEAD
1925 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1926 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1927
1928 for line in output.splitlines():
1929 lhs, rhs = line.split('\t', 1)
1930 if rhs == 'HEAD' and lhs.startswith('ref:'):
1931 return lhs[4:].strip()
1932
1933 return None
1934
Zac Livingstone4332262017-06-16 08:56:09 -06001935 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001936 try:
1937 # if revision (sha or tag) is not present then following function
1938 # throws an error.
1939 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1940 return True
1941 except GitError:
1942 # There is no such persistent revision. We have to fetch it.
1943 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001944
Julien Campergue335f5ef2013-10-16 11:02:35 +02001945 def _FetchArchive(self, tarpath, cwd=None):
1946 cmd = ['archive', '-v', '-o', tarpath]
1947 cmd.append('--remote=%s' % self.remote.url)
1948 cmd.append('--prefix=%s/' % self.relpath)
1949 cmd.append(self.revisionExpr)
1950
1951 command = GitCommand(self, cmd, cwd=cwd,
1952 capture_stdout=True,
1953 capture_stderr=True)
1954
1955 if command.Wait() != 0:
1956 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1957
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001958 def _RemoteFetch(self, name=None,
1959 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001960 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001961 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001962 verbose=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001963 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001964 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001965 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001966 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001967 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001968 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001969 clone_filter=None,
1970 retry_fetches=2,
1971 retry_sleep_initial_sec=4.0,
1972 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001973 is_sha1 = False
1974 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001975 # The depth should not be used when fetching to a mirror because
1976 # it will result in a shallow repository that cannot be cloned or
1977 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001978 # The repo project should also never be synced with partial depth.
1979 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1980 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001981
Shawn Pearce69e04d82014-01-29 12:48:54 -08001982 if depth:
1983 current_branch_only = True
1984
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001985 if ID_RE.match(self.revisionExpr) is not None:
1986 is_sha1 = True
1987
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001988 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001989 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001990 # this is a tag and its sha1 value should never change
1991 tag_name = self.revisionExpr[len(R_TAGS):]
1992
1993 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001994 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05001995 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001996 print('Skipped fetching project %s (already have persistent ref)'
1997 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001998 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001999 if is_sha1 and not depth:
2000 # When syncing a specific commit and --depth is not set:
2001 # * if upstream is explicitly specified and is not a sha1, fetch only
2002 # upstream as users expect only upstream to be fetch.
2003 # Note: The commit might not be in upstream in which case the sync
2004 # will fail.
2005 # * otherwise, fetch all branches to make sure we end up with the
2006 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002007 if self.upstream:
2008 current_branch_only = not ID_RE.match(self.upstream)
2009 else:
2010 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002011
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002012 if not name:
2013 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002014
2015 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002016 remote = self.GetRemote(name)
2017 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002018 ssh_proxy = True
2019
Shawn O. Pearce88443382010-10-08 10:02:09 +02002020 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002021 if alt_dir and 'objects' == os.path.basename(alt_dir):
2022 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002023 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2024 remote = self.GetRemote(name)
2025
David Pursehouse8a68ff92012-09-24 12:15:13 +09002026 all_refs = self.bare_ref.all
2027 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002028 tmp = set()
2029
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302030 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002031 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002032 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002033 all_refs[r] = ref_id
2034 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002035 continue
2036
David Pursehouse8a68ff92012-09-24 12:15:13 +09002037 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038 continue
2039
David Pursehouse8a68ff92012-09-24 12:15:13 +09002040 r = 'refs/_alt/%s' % ref_id
2041 all_refs[r] = ref_id
2042 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002043 tmp.add(r)
2044
heping3d7bbc92017-04-12 19:51:47 +08002045 tmp_packed_lines = []
2046 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002047
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302048 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002049 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002050 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002051 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002052 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002053
heping3d7bbc92017-04-12 19:51:47 +08002054 tmp_packed = ''.join(tmp_packed_lines)
2055 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002056 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002057 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002058 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002059
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002060 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002061
Xin Li745be2e2019-06-03 11:24:30 -07002062 if clone_filter:
2063 git_require((2, 19, 0), fail=True, msg='partial clones')
2064 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002065 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002066
Conley Owensf97e8382015-01-21 11:12:46 -08002067 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002068 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002069 else:
2070 # If this repo has shallow objects, then we don't know which refs have
2071 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2072 # do this with projects that don't have shallow objects, since it is less
2073 # efficient.
2074 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2075 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002076
Mike Frysinger4847e052020-02-22 00:07:35 -05002077 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002078 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002079 if not quiet and sys.stdout.isatty():
2080 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002081 if not self.worktree:
2082 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002083 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002084
Mike Frysingere57f1142019-03-18 21:27:54 -04002085 if force_sync:
2086 cmd.append('--force')
2087
David Pursehouse74cfd272015-10-14 10:50:15 +09002088 if prune:
2089 cmd.append('--prune')
2090
Martin Kellye4e94d22017-03-21 16:05:12 -07002091 if submodules:
2092 cmd.append('--recurse-submodules=on-demand')
2093
Kuang-che Wu6856f982019-11-25 12:37:55 +08002094 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002095 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002096 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002097 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002098 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002099 spec.append('tag')
2100 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002101
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302102 if self.manifest.IsMirror and not current_branch_only:
2103 branch = None
2104 else:
2105 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002106 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002107 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002108 # Shallow checkout of a specific commit, fetch from that commit and not
2109 # the heads only as the commit might be deeper in the history.
2110 spec.append(branch)
2111 else:
2112 if is_sha1:
2113 branch = self.upstream
2114 if branch is not None and branch.strip():
2115 if not branch.startswith('refs/'):
2116 branch = R_HEADS + branch
2117 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2118
2119 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2120 # whole repo.
2121 if self.manifest.IsMirror and not spec:
2122 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2123
2124 # If using depth then we should not get all the tags since they may
2125 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002126 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002127 cmd.append('--no-tags')
2128 else:
2129 cmd.append('--tags')
2130 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2131
Conley Owens80b87fe2014-05-09 17:13:44 -07002132 cmd.extend(spec)
2133
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002134 # At least one retry minimum due to git remote prune.
2135 retry_fetches = max(retry_fetches, 2)
2136 retry_cur_sleep = retry_sleep_initial_sec
2137 ok = prune_tried = False
2138 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002139 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger4847e052020-02-22 00:07:35 -05002140 merge_output=True, capture_stdout=quiet)
John L. Villalovos126e2982015-01-29 21:58:12 -08002141 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002142 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002143 ok = True
2144 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002145
2146 # Retry later due to HTTP 429 Too Many Requests.
2147 elif ('error:' in gitcmd.stderr and
2148 'HTTP 429' in gitcmd.stderr):
2149 if not quiet:
2150 print('429 received, sleeping: %s sec' % retry_cur_sleep,
2151 file=sys.stderr)
2152 time.sleep(retry_cur_sleep)
2153 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2154 MAXIMUM_RETRY_SLEEP_SEC)
2155 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2156 RETRY_JITTER_PERCENT))
2157 continue
2158
2159 # If this is not last attempt, try 'git remote prune'.
2160 elif (try_n < retry_fetches - 1 and
2161 'error:' in gitcmd.stderr and
2162 'git remote prune' in gitcmd.stderr and
2163 not prune_tried):
2164 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002165 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002166 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002167 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002168 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002169 break
2170 continue
Brian Harring14a66742012-09-28 20:21:57 -07002171 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002172 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2173 # in sha1 mode, we just tried sync'ing from the upstream field; it
2174 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002175 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002176 elif ret < 0:
2177 # Git died with a signal, exit immediately
2178 break
Mike Frysinger31990f02020-02-17 01:35:18 -05002179 if not verbose:
2180 print('%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002181 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002182
2183 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002184 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002185 if old_packed != '':
2186 _lwrite(packed_refs, old_packed)
2187 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002188 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002189 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002190
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002191 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002192 # We just synced the upstream given branch; verify we
2193 # got what we wanted, else trigger a second run of all
2194 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002195 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002196 # Sync the current branch only with depth set to None.
2197 # We always pass depth=None down to avoid infinite recursion.
2198 return self._RemoteFetch(
2199 name=name, quiet=quiet, verbose=verbose,
2200 current_branch_only=current_branch_only and depth,
2201 initial=False, alt_dir=alt_dir,
2202 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002203
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002204 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002205
Mike Frysingere50b6a72020-02-19 01:45:48 -05002206 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002207 if initial and \
2208 (self.manifest.manifestProject.config.GetString('repo.depth') or
2209 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002210 return False
2211
2212 remote = self.GetRemote(self.remote.name)
2213 bundle_url = remote.url + '/clone.bundle'
2214 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002215 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2216 'persistent-http',
2217 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002218 return False
2219
2220 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2221 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2222
2223 exist_dst = os.path.exists(bundle_dst)
2224 exist_tmp = os.path.exists(bundle_tmp)
2225
2226 if not initial and not exist_dst and not exist_tmp:
2227 return False
2228
2229 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002230 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2231 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002232 if not exist_dst:
2233 return False
2234
2235 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002236 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002237 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002238 if not quiet and sys.stdout.isatty():
2239 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002240 if not self.worktree:
2241 cmd.append('--update-head-ok')
2242 cmd.append(bundle_dst)
2243 for f in remote.fetch:
2244 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002245 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002246
2247 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002248 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002249 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002251 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002252 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002253
Mike Frysingere50b6a72020-02-19 01:45:48 -05002254 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002255 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002256 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002257
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002258 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002259 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002260 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002261 if os.path.exists(tmpPath):
2262 size = os.stat(tmpPath).st_size
2263 if size >= 1024:
2264 cmd += ['--continue-at', '%d' % (size,)]
2265 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002266 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002267 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002268 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002269 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002270 if proxy:
2271 cmd += ['--proxy', proxy]
2272 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2273 cmd += ['--proxy', os.environ['http_proxy']]
2274 if srcUrl.startswith('persistent-https'):
2275 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2276 elif srcUrl.startswith('persistent-http'):
2277 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002278 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002279
Dave Borowitz137d0132015-01-02 11:12:54 -08002280 if IsTrace():
2281 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002282 if verbose:
2283 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2284 stdout = None if verbose else subprocess.PIPE
2285 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002286 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002287 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002288 except OSError:
2289 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002290
Mike Frysingere50b6a72020-02-19 01:45:48 -05002291 (output, _) = proc.communicate()
2292 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002293
Dave Borowitz137d0132015-01-02 11:12:54 -08002294 if curlret == 22:
2295 # From curl man page:
2296 # 22: HTTP page not retrieved. The requested url was not found or
2297 # returned another error with the HTTP error code being 400 or above.
2298 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002299 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002300 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2301 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002302 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002303 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002304 elif curlret and not verbose and output:
2305 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002306
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002307 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002308 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002309 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002310 return True
2311 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002312 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002313 return False
2314 else:
2315 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002316
Kris Giesingc8d882a2014-12-23 13:02:32 -08002317 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002318 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002319 with open(path, 'rb') as f:
2320 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002321 return True
2322 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002323 if not quiet:
2324 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002325 return False
2326 except OSError:
2327 return False
2328
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002329 def _Checkout(self, rev, quiet=False):
2330 cmd = ['checkout']
2331 if quiet:
2332 cmd.append('-q')
2333 cmd.append(rev)
2334 cmd.append('--')
2335 if GitCommand(self, cmd).Wait() != 0:
2336 if self._allrefs:
2337 raise GitError('%s checkout %s ' % (self.name, rev))
2338
Mike Frysinger915fda12020-03-22 12:15:20 -04002339 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002340 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002341 if ffonly:
2342 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002343 if record_origin:
2344 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002345 cmd.append(rev)
2346 cmd.append('--')
2347 if GitCommand(self, cmd).Wait() != 0:
2348 if self._allrefs:
2349 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2350
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302351 def _LsRemote(self, refs):
2352 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302353 p = GitCommand(self, cmd, capture_stdout=True)
2354 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002355 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302356 return None
2357
Anthony King7bdac712014-07-16 12:56:40 +01002358 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002359 cmd = ['revert']
2360 cmd.append('--no-edit')
2361 cmd.append(rev)
2362 cmd.append('--')
2363 if GitCommand(self, cmd).Wait() != 0:
2364 if self._allrefs:
2365 raise GitError('%s revert %s ' % (self.name, rev))
2366
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002367 def _ResetHard(self, rev, quiet=True):
2368 cmd = ['reset', '--hard']
2369 if quiet:
2370 cmd.append('-q')
2371 cmd.append(rev)
2372 if GitCommand(self, cmd).Wait() != 0:
2373 raise GitError('%s reset --hard %s ' % (self.name, rev))
2374
Martin Kellye4e94d22017-03-21 16:05:12 -07002375 def _SyncSubmodules(self, quiet=True):
2376 cmd = ['submodule', 'update', '--init', '--recursive']
2377 if quiet:
2378 cmd.append('-q')
2379 if GitCommand(self, cmd).Wait() != 0:
2380 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2381
Anthony King7bdac712014-07-16 12:56:40 +01002382 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002383 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002384 if onto is not None:
2385 cmd.extend(['--onto', onto])
2386 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002387 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002388 raise GitError('%s rebase %s ' % (self.name, upstream))
2389
Pierre Tardy3d125942012-05-04 12:18:12 +02002390 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002391 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002392 if ffonly:
2393 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002394 if GitCommand(self, cmd).Wait() != 0:
2395 raise GitError('%s merge %s ' % (self.name, head))
2396
David Pursehousee8ace262020-02-13 12:41:15 +09002397 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002398 init_git_dir = not os.path.exists(self.gitdir)
2399 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002400 try:
2401 # Initialize the bare repository, which contains all of the objects.
2402 if init_obj_dir:
2403 os.makedirs(self.objdir)
2404 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002405
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002406 if self.use_git_worktrees:
2407 # Set up the m/ space to point to the worktree-specific ref space.
2408 # We'll update the worktree-specific ref space on each checkout.
2409 if self.manifest.branch:
2410 self.bare_git.symbolic_ref(
2411 '-m', 'redirecting to worktree scope',
2412 R_M + self.manifest.branch,
2413 R_WORKTREE_M + self.manifest.branch)
2414
2415 # Enable per-worktree config file support if possible. This is more a
2416 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002417 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002418 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002419
Kevin Degib1a07b82015-07-27 13:33:43 -06002420 # If we have a separate directory to hold refs, initialize it as well.
2421 if self.objdir != self.gitdir:
2422 if init_git_dir:
2423 os.makedirs(self.gitdir)
2424
2425 if init_obj_dir or init_git_dir:
2426 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2427 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002428 try:
2429 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2430 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002431 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002432 print("Retrying clone after deleting %s" %
2433 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002434 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002435 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2436 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002437 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002438 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002439 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2440 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002441 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002442 raise e
2443 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002444
Kevin Degi384b3c52014-10-16 16:02:58 -06002445 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002446 mp = self.manifest.manifestProject
2447 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002448
Kevin Degib1a07b82015-07-27 13:33:43 -06002449 if ref_dir or mirror_git:
2450 if not mirror_git:
2451 mirror_git = os.path.join(ref_dir, self.name + '.git')
2452 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2453 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002454 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2455 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002456
Kevin Degib1a07b82015-07-27 13:33:43 -06002457 if os.path.exists(mirror_git):
2458 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002459 elif os.path.exists(repo_git):
2460 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002461 elif os.path.exists(worktrees_git):
2462 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002463 else:
2464 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002465
Kevin Degib1a07b82015-07-27 13:33:43 -06002466 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002467 if not os.path.isabs(ref_dir):
2468 # The alternate directory is relative to the object database.
2469 ref_dir = os.path.relpath(ref_dir,
2470 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002471 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2472 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002473
David Pursehousee8ace262020-02-13 12:41:15 +09002474 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002475
2476 m = self.manifest.manifestProject.config
2477 for key in ['user.name', 'user.email']:
2478 if m.Has(key, include_defaults=False):
2479 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002480 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002481 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002482 if self.manifest.IsMirror:
2483 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002484 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002485 self.config.SetString('core.bare', None)
2486 except Exception:
2487 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002488 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002489 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002490 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002491 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002492
David Pursehousee8ace262020-02-13 12:41:15 +09002493 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002494 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002495 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002496
David Pursehousee8ace262020-02-13 12:41:15 +09002497 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002498 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002499 if not os.path.exists(hooks):
2500 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002501 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002502 name = os.path.basename(stock_hook)
2503
Victor Boivie65e0f352011-04-18 11:23:29 +02002504 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002505 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002506 # Don't install a Gerrit Code Review hook if this
2507 # project does not appear to use it for reviews.
2508 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002509 # Since the manifest project is one of those, but also
2510 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002511 continue
2512
2513 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002514 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002515 continue
2516 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002517 # If the files are the same, we'll leave it alone. We create symlinks
2518 # below by default but fallback to hardlinks if the OS blocks them.
2519 # So if we're here, it's probably because we made a hardlink below.
2520 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002521 if not quiet:
2522 _warn("%s: Not replacing locally modified %s hook",
2523 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002524 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002525 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002526 platform_utils.symlink(
2527 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002528 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002529 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002530 try:
2531 os.link(stock_hook, dst)
2532 except OSError:
2533 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002534 else:
2535 raise
2536
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002537 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002538 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002540 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002541 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002542 remote.review = self.remote.review
2543 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002544
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002545 if self.worktree:
2546 remote.ResetFetch(mirror=False)
2547 else:
2548 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002549 remote.Save()
2550
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002551 def _InitMRef(self):
2552 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002553 if self.use_git_worktrees:
2554 # We can't update this ref with git worktrees until it exists.
2555 # We'll wait until the initial checkout to set it.
2556 if not os.path.exists(self.worktree):
2557 return
2558
2559 base = R_WORKTREE_M
2560 active_git = self.work_git
2561 else:
2562 base = R_M
2563 active_git = self.bare_git
2564
2565 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002566
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002567 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002568 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002569
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002570 def _InitAnyMRef(self, ref, active_git):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002571 cur = self.bare_ref.symref(ref)
2572
2573 if self.revisionId:
2574 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2575 msg = 'manifest set to %s' % self.revisionId
2576 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002577 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002578 else:
2579 remote = self.GetRemote(self.remote.name)
2580 dst = remote.ToLocal(self.revisionExpr)
2581 if cur != dst:
2582 msg = 'manifest set to %s' % self.revisionExpr
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002583 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002584
Kevin Degi384b3c52014-10-16 16:02:58 -06002585 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002586 # Git worktrees don't use symlinks to share at all.
2587 if self.use_git_worktrees:
2588 return
2589
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002590 symlink_files = self.shareable_files[:]
2591 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002592 if share_refs:
2593 symlink_files += self.working_tree_files
2594 symlink_dirs += self.working_tree_dirs
2595 to_symlink = symlink_files + symlink_dirs
2596 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002597 # Try to self-heal a bit in simple cases.
2598 dst_path = os.path.join(destdir, name)
2599 src_path = os.path.join(srcdir, name)
2600
2601 if name in self.working_tree_dirs:
2602 # If the dir is missing under .repo/projects/, create it.
2603 if not os.path.exists(src_path):
2604 os.makedirs(src_path)
2605
2606 elif name in self.working_tree_files:
2607 # If it's a file under the checkout .git/ and the .repo/projects/ has
2608 # nothing, move the file under the .repo/projects/ tree.
2609 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2610 platform_utils.rename(dst_path, src_path)
2611
2612 # If the path exists under the .repo/projects/ and there's no symlink
2613 # under the checkout .git/, recreate the symlink.
2614 if name in self.working_tree_dirs or name in self.working_tree_files:
2615 if os.path.exists(src_path) and not os.path.exists(dst_path):
2616 platform_utils.symlink(
2617 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2618
2619 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002620 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002621 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002622 # Fail if the links are pointing to the wrong place
2623 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002624 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002625 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002626 'work tree. If you\'re comfortable with the '
2627 'possibility of losing the work tree\'s git metadata,'
2628 ' use `repo sync --force-sync {0}` to '
2629 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002630
David James8d201162013-10-11 17:03:19 -07002631 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2632 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2633
2634 Args:
2635 gitdir: The bare git repository. Must already be initialized.
2636 dotgit: The repository you would like to initialize.
2637 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2638 Only one work tree can store refs under a given |gitdir|.
2639 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2640 This saves you the effort of initializing |dotgit| yourself.
2641 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002642 symlink_files = self.shareable_files[:]
2643 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002644 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002645 symlink_files += self.working_tree_files
2646 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002647 to_symlink = symlink_files + symlink_dirs
2648
2649 to_copy = []
2650 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002651 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002652
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002653 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002654 for name in set(to_copy).union(to_symlink):
2655 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002656 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002657 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002658
Kevin Degi384b3c52014-10-16 16:02:58 -06002659 if os.path.lexists(dst):
2660 continue
David James8d201162013-10-11 17:03:19 -07002661
2662 # If the source dir doesn't exist, create an empty dir.
2663 if name in symlink_dirs and not os.path.lexists(src):
2664 os.makedirs(src)
2665
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002666 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002667 platform_utils.symlink(
2668 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002669 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002670 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002671 shutil.copytree(src, dst)
2672 elif os.path.isfile(src):
2673 shutil.copy(src, dst)
2674
Conley Owens80b87fe2014-05-09 17:13:44 -07002675 # If the source file doesn't exist, ensure the destination
2676 # file doesn't either.
2677 if name in symlink_files and not os.path.lexists(src):
2678 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002679 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002680 except OSError:
2681 pass
2682
David James8d201162013-10-11 17:03:19 -07002683 except OSError as e:
2684 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002685 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002686 else:
2687 raise
2688
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002689 def _InitGitWorktree(self):
2690 """Init the project using git worktrees."""
2691 self.bare_git.worktree('prune')
2692 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2693 self.worktree, self.GetRevisionId())
2694
2695 # Rewrite the internal state files to use relative paths between the
2696 # checkouts & worktrees.
2697 dotgit = os.path.join(self.worktree, '.git')
2698 with open(dotgit, 'r') as fp:
2699 # Figure out the checkout->worktree path.
2700 setting = fp.read()
2701 assert setting.startswith('gitdir:')
2702 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002703 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2704 # of file permissions. Delete it and recreate it from scratch to avoid.
2705 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002706 # Use relative path from checkout->worktree & maintain Unix line endings
2707 # on all OS's to match git behavior.
2708 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002709 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2710 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002711 # Use relative path from worktree->checkout & maintain Unix line endings
2712 # on all OS's to match git behavior.
2713 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002714 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2715
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002716 self._InitMRef()
2717
Martin Kellye4e94d22017-03-21 16:05:12 -07002718 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002719 realdotgit = os.path.join(self.worktree, '.git')
2720 tmpdotgit = realdotgit + '.tmp'
2721 init_dotgit = not os.path.exists(realdotgit)
2722 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002723 if self.use_git_worktrees:
2724 self._InitGitWorktree()
2725 self._CopyAndLinkFiles()
2726 return
2727
Mike Frysingerf4545122019-11-11 04:34:16 -05002728 dotgit = tmpdotgit
2729 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2730 os.makedirs(tmpdotgit)
2731 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2732 copy_all=False)
2733 else:
2734 dotgit = realdotgit
2735
Kevin Degib1a07b82015-07-27 13:33:43 -06002736 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002737 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2738 except GitError as e:
2739 if force_sync and not init_dotgit:
2740 try:
2741 platform_utils.rmtree(dotgit)
2742 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002743 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002744 raise e
2745 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746
Mike Frysingerf4545122019-11-11 04:34:16 -05002747 if init_dotgit:
2748 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002749
Mike Frysingerf4545122019-11-11 04:34:16 -05002750 # Now that the .git dir is fully set up, move it to its final home.
2751 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002752
Mike Frysingerf4545122019-11-11 04:34:16 -05002753 # Finish checking out the worktree.
2754 cmd = ['read-tree', '--reset', '-u']
2755 cmd.append('-v')
2756 cmd.append(HEAD)
2757 if GitCommand(self, cmd).Wait() != 0:
2758 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002759
Mike Frysingerf4545122019-11-11 04:34:16 -05002760 if submodules:
2761 self._SyncSubmodules(quiet=True)
2762 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002763
Renaud Paquay788e9622017-01-27 11:41:12 -08002764 def _get_symlink_error_message(self):
2765 if platform_utils.isWindows():
2766 return ('Unable to create symbolic link. Please re-run the command as '
2767 'Administrator, or see '
2768 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2769 'for other options.')
2770 return 'filesystem must support symlinks'
2771
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002772 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002773 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002774
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002775 def _revlist(self, *args, **kw):
2776 a = []
2777 a.extend(args)
2778 a.append('--')
2779 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002780
2781 @property
2782 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002783 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002784
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002785 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002786 """Get logs between two revisions of this project."""
2787 comp = '..'
2788 if rev1:
2789 revs = [rev1]
2790 if rev2:
2791 revs.extend([comp, rev2])
2792 cmd = ['log', ''.join(revs)]
2793 out = DiffColoring(self.config)
2794 if out.is_on and color:
2795 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002796 if pretty_format is not None:
2797 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002798 if oneline:
2799 cmd.append('--oneline')
2800
2801 try:
2802 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2803 if log.Wait() == 0:
2804 return log.stdout
2805 except GitError:
2806 # worktree may not exist if groups changed for example. In that case,
2807 # try in gitdir instead.
2808 if not os.path.exists(self.worktree):
2809 return self.bare_git.log(*cmd[1:])
2810 else:
2811 raise
2812 return None
2813
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002814 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2815 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002816 """Get the list of logs from this revision to given revisionId"""
2817 logs = {}
2818 selfId = self.GetRevisionId(self._allrefs)
2819 toId = toProject.GetRevisionId(toProject._allrefs)
2820
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002821 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2822 pretty_format=pretty_format)
2823 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2824 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002825 return logs
2826
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002827 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002828
David James8d201162013-10-11 17:03:19 -07002829 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002830 self._project = project
2831 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002832 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002833
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002834 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2835 def __getstate__(self):
2836 return (self._project, self._bare, self._gitdir)
2837
2838 def __setstate__(self, state):
2839 self._project, self._bare, self._gitdir = state
2840
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002841 def LsOthers(self):
2842 p = GitCommand(self._project,
2843 ['ls-files',
2844 '-z',
2845 '--others',
2846 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002847 bare=False,
David James8d201162013-10-11 17:03:19 -07002848 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002849 capture_stdout=True,
2850 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002851 if p.Wait() == 0:
2852 out = p.stdout
2853 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002854 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002855 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002856 return []
2857
2858 def DiffZ(self, name, *args):
2859 cmd = [name]
2860 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002861 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002862 cmd.extend(args)
2863 p = GitCommand(self._project,
2864 cmd,
David James8d201162013-10-11 17:03:19 -07002865 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002866 bare=False,
2867 capture_stdout=True,
2868 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002869 try:
2870 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002871 if not hasattr(out, 'encode'):
2872 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002873 r = {}
2874 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002875 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002876 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002877 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002878 info = next(out)
2879 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002880 except StopIteration:
2881 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002882
2883 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002884
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002885 def __init__(self, path, omode, nmode, oid, nid, state):
2886 self.path = path
2887 self.src_path = None
2888 self.old_mode = omode
2889 self.new_mode = nmode
2890 self.old_id = oid
2891 self.new_id = nid
2892
2893 if len(state) == 1:
2894 self.status = state
2895 self.level = None
2896 else:
2897 self.status = state[:1]
2898 self.level = state[1:]
2899 while self.level.startswith('0'):
2900 self.level = self.level[1:]
2901
2902 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002903 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002904 if info.status in ('R', 'C'):
2905 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002906 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002907 r[info.path] = info
2908 return r
2909 finally:
2910 p.Wait()
2911
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002912 def GetDotgitPath(self, subpath=None):
2913 """Return the full path to the .git dir.
2914
2915 As a convenience, append |subpath| if provided.
2916 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002917 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002918 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002919 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002920 dotgit = os.path.join(self._project.worktree, '.git')
2921 if os.path.isfile(dotgit):
2922 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2923 with open(dotgit) as fp:
2924 setting = fp.read()
2925 assert setting.startswith('gitdir:')
2926 gitdir = setting.split(':', 1)[1].strip()
2927 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2928
2929 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2930
2931 def GetHead(self):
2932 """Return the ref that HEAD points to."""
2933 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002934 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002935 with open(path) as fd:
2936 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002937 except IOError as e:
2938 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002939 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302940 line = line.decode()
2941 except AttributeError:
2942 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002943 if line.startswith('ref: '):
2944 return line[5:-1]
2945 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002946
2947 def SetHead(self, ref, message=None):
2948 cmdv = []
2949 if message is not None:
2950 cmdv.extend(['-m', message])
2951 cmdv.append(HEAD)
2952 cmdv.append(ref)
2953 self.symbolic_ref(*cmdv)
2954
2955 def DetachHead(self, new, message=None):
2956 cmdv = ['--no-deref']
2957 if message is not None:
2958 cmdv.extend(['-m', message])
2959 cmdv.append(HEAD)
2960 cmdv.append(new)
2961 self.update_ref(*cmdv)
2962
2963 def UpdateRef(self, name, new, old=None,
2964 message=None,
2965 detach=False):
2966 cmdv = []
2967 if message is not None:
2968 cmdv.extend(['-m', message])
2969 if detach:
2970 cmdv.append('--no-deref')
2971 cmdv.append(name)
2972 cmdv.append(new)
2973 if old is not None:
2974 cmdv.append(old)
2975 self.update_ref(*cmdv)
2976
2977 def DeleteRef(self, name, old=None):
2978 if not old:
2979 old = self.rev_parse(name)
2980 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002981 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002982
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002983 def rev_list(self, *args, **kw):
2984 if 'format' in kw:
2985 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2986 else:
2987 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002988 cmdv.extend(args)
2989 p = GitCommand(self._project,
2990 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002991 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002992 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002993 capture_stdout=True,
2994 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002995 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002996 raise GitError('%s rev-list %s: %s' %
2997 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002998 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002999
3000 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003001 """Allow arbitrary git commands using pythonic syntax.
3002
3003 This allows you to do things like:
3004 git_obj.rev_parse('HEAD')
3005
3006 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3007 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003008 Any other positional arguments will be passed to the git command, and the
3009 following keyword arguments are supported:
3010 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003011
3012 Args:
3013 name: The name of the git command to call. Any '_' characters will
3014 be replaced with '-'.
3015
3016 Returns:
3017 A callable object that will try to call git with the named command.
3018 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003019 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003020
Dave Borowitz091f8932012-10-23 17:01:04 -07003021 def runner(*args, **kwargs):
3022 cmdv = []
3023 config = kwargs.pop('config', None)
3024 for k in kwargs:
3025 raise TypeError('%s() got an unexpected keyword argument %r'
3026 % (name, k))
3027 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303028 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003029 cmdv.append('-c')
3030 cmdv.append('%s=%s' % (k, v))
3031 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003032 cmdv.extend(args)
3033 p = GitCommand(self._project,
3034 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003035 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003036 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003037 capture_stdout=True,
3038 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003039 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003040 raise GitError('%s %s: %s' %
3041 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003042 r = p.stdout
3043 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3044 return r[:-1]
3045 return r
3046 return runner
3047
3048
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003049class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003050
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003051 def __str__(self):
3052 return 'prior sync failed; rebase still in progress'
3053
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003054
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003055class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003056
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003057 def __str__(self):
3058 return 'contains uncommitted changes'
3059
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003060
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003061class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003062
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003063 def __init__(self, project, text):
3064 self.project = project
3065 self.text = text
3066
3067 def Print(self, syncbuf):
3068 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3069 syncbuf.out.nl()
3070
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003071
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003072class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003073
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003074 def __init__(self, project, why):
3075 self.project = project
3076 self.why = why
3077
3078 def Print(self, syncbuf):
3079 syncbuf.out.fail('error: %s/: %s',
3080 self.project.relpath,
3081 str(self.why))
3082 syncbuf.out.nl()
3083
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003084
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003085class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003086
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003087 def __init__(self, project, action):
3088 self.project = project
3089 self.action = action
3090
3091 def Run(self, syncbuf):
3092 out = syncbuf.out
3093 out.project('project %s/', self.project.relpath)
3094 out.nl()
3095 try:
3096 self.action()
3097 out.nl()
3098 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003099 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003100 out.nl()
3101 return False
3102
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003103
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003104class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003105
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106 def __init__(self, config):
3107 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003108 self.project = self.printer('header', attr='bold')
3109 self.info = self.printer('info')
3110 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003111
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003112
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003113class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003114
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003115 def __init__(self, config, detach_head=False):
3116 self._messages = []
3117 self._failures = []
3118 self._later_queue1 = []
3119 self._later_queue2 = []
3120
3121 self.out = _SyncColoring(config)
3122 self.out.redirect(sys.stderr)
3123
3124 self.detach_head = detach_head
3125 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003126 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003127
3128 def info(self, project, fmt, *args):
3129 self._messages.append(_InfoMessage(project, fmt % args))
3130
3131 def fail(self, project, err=None):
3132 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003133 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003134
3135 def later1(self, project, what):
3136 self._later_queue1.append(_Later(project, what))
3137
3138 def later2(self, project, what):
3139 self._later_queue2.append(_Later(project, what))
3140
3141 def Finish(self):
3142 self._PrintMessages()
3143 self._RunLater()
3144 self._PrintMessages()
3145 return self.clean
3146
David Rileye0684ad2017-04-05 00:02:59 -07003147 def Recently(self):
3148 recent_clean = self.recent_clean
3149 self.recent_clean = True
3150 return recent_clean
3151
3152 def _MarkUnclean(self):
3153 self.clean = False
3154 self.recent_clean = False
3155
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003156 def _RunLater(self):
3157 for q in ['_later_queue1', '_later_queue2']:
3158 if not self._RunQueue(q):
3159 return
3160
3161 def _RunQueue(self, queue):
3162 for m in getattr(self, queue):
3163 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003164 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003165 return False
3166 setattr(self, queue, [])
3167 return True
3168
3169 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003170 if self._messages or self._failures:
3171 if os.isatty(2):
3172 self.out.write(progress.CSI_ERASE_LINE)
3173 self.out.write('\r')
3174
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003175 for m in self._messages:
3176 m.Print(self)
3177 for m in self._failures:
3178 m.Print(self)
3179
3180 self._messages = []
3181 self._failures = []
3182
3183
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003184class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003185
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003186 """A special project housed under .repo.
3187 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003188
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003189 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003190 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003191 manifest=manifest,
3192 name=name,
3193 gitdir=gitdir,
3194 objdir=gitdir,
3195 worktree=worktree,
3196 remote=RemoteSpec('origin'),
3197 relpath='.repo/%s' % name,
3198 revisionExpr='refs/heads/master',
3199 revisionId=None,
3200 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003201
3202 def PreSync(self):
3203 if self.Exists:
3204 cb = self.CurrentBranch
3205 if cb:
3206 base = self.GetBranch(cb).merge
3207 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003208 self.revisionExpr = base
3209 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003210
Martin Kelly224a31a2017-07-10 14:46:25 -07003211 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003212 """ Prepare MetaProject for manifest branch switch
3213 """
3214
3215 # detach and delete manifest branch, allowing a new
3216 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003217 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003218 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003219 syncbuf.Finish()
3220
3221 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003222 ['update-ref', '-d', 'refs/heads/default'],
3223 capture_stdout=True,
3224 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003225
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003226 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003227 def LastFetch(self):
3228 try:
3229 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3230 return os.path.getmtime(fh)
3231 except OSError:
3232 return 0
3233
3234 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003235 def HasChanges(self):
3236 """Has the remote received new commits not yet checked out?
3237 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003238 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003239 return False
3240
David Pursehouse8a68ff92012-09-24 12:15:13 +09003241 all_refs = self.bare_ref.all
3242 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003243 head = self.work_git.GetHead()
3244 if head.startswith(R_HEADS):
3245 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003246 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003247 except KeyError:
3248 head = None
3249
3250 if revid == head:
3251 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003252 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003253 return True
3254 return False