blob: e849af539c3f37296b98ed5a559a7e37203e2145 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070034 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070036from error import CacheApplyError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080037from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070040from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041
Shawn O. Pearced237b692009-04-17 18:49:50 -070042from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
David Pursehouse59bbb582013-05-17 10:49:33 +090044from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040045if is_python3():
46 import urllib.parse
47else:
48 import imp
49 import urlparse
50 urllib = imp.new_module('urllib')
51 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070054
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055def _lwrite(path, content):
56 lock = '%s.lock' % path
57
Chirayu Desai303a82f2014-08-19 22:57:17 +053058 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 try:
60 fd.write(content)
61 finally:
62 fd.close()
63
64 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070065 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070066 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080067 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 raise
69
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
Shawn O. Pearce48244782009-04-16 08:25:57 -070071def _error(fmt, *args):
72 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070073 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
David Pursehousef33929d2015-08-24 14:39:14 +090076def _warn(fmt, *args):
77 msg = fmt % args
78 print('warn: %s' % msg, file=sys.stderr)
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081def not_rev(r):
82 return '^' + r
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080085def sq(r):
86 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080087
Jonathan Nieder93719792015-03-17 11:29:58 -070088_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
90
Jonathan Nieder93719792015-03-17 11:29:58 -070091def _ProjectHooks():
92 """List the hooks present in the 'hooks' directory.
93
94 These hooks are project hooks and are copied to the '.git/hooks' directory
95 of all subprojects.
96
97 This function caches the list of hooks (based on the contents of the
98 'repo/hooks' directory) on the first call.
99
100 Returns:
101 A list of absolute paths to all of the files in the hooks directory.
102 """
103 global _project_hook_list
104 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700105 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700106 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700107 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 return _project_hook_list
109
110
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700111class DownloadedChange(object):
112 _commit_cache = None
113
114 def __init__(self, project, base, change_id, ps_id, commit):
115 self.project = project
116 self.base = base
117 self.change_id = change_id
118 self.ps_id = ps_id
119 self.commit = commit
120
121 @property
122 def commits(self):
123 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700124 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
125 '--abbrev-commit',
126 '--pretty=oneline',
127 '--reverse',
128 '--date-order',
129 not_rev(self.base),
130 self.commit,
131 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700132 return self._commit_cache
133
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135class ReviewableBranch(object):
136 _commit_cache = None
137
138 def __init__(self, project, branch, base):
139 self.project = project
140 self.branch = branch
141 self.base = base
142
143 @property
144 def name(self):
145 return self.branch.name
146
147 @property
148 def commits(self):
149 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700150 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
151 '--abbrev-commit',
152 '--pretty=oneline',
153 '--reverse',
154 '--date-order',
155 not_rev(self.base),
156 R_HEADS + self.name,
157 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700176 def UploadForReview(self, people,
177 auto_topic=False,
178 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200179 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700180 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700190 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200192 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800193 validate_certs=validate_certs,
194 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700195
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700196 def GetPublishedRefs(self):
197 refs = {}
198 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700199 self.branch.remote.SshReviewUrl(self.project.UserEmail),
200 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700201 for line in output.split('\n'):
202 try:
203 (sha, ref) = line.split()
204 refs[sha] = ref
205 except ValueError:
206 pass
207
208 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700209
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700210
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213 def __init__(self, config):
214 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100215 self.project = self.printer('header', attr='bold')
216 self.branch = self.printer('header', attr='bold')
217 self.nobranch = self.printer('nobranch', fg='red')
218 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219
Anthony King7bdac712014-07-16 12:56:40 +0100220 self.added = self.printer('added', fg='green')
221 self.changed = self.printer('changed', fg='red')
222 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700223
224
225class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700226
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227 def __init__(self, config):
228 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100229 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
Anthony King7bdac712014-07-16 12:56:40 +0100232class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
James W. Mills24c13082012-04-12 15:04:13 -0500234 def __init__(self, name, value, keep):
235 self.name = name
236 self.value = value
237 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700239
Anthony King7bdac712014-07-16 12:56:40 +0100240class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243 self.src = src
244 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800245 self.abs_src = abssrc
246 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247
248 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800249 src = self.abs_src
250 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 # copy file if it does not exist or is out of date
252 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
253 try:
254 # remove existing file first, since it might be read-only
255 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800256 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400257 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200258 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700259 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700261 shutil.copy(src, dest)
262 # make the file read-only
263 mode = os.stat(dest)[stat.ST_MODE]
264 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
265 os.chmod(dest, mode)
266 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700267 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700269
Anthony King7bdac712014-07-16 12:56:40 +0100270class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Wink Saville4c426ef2015-06-03 08:05:17 -0700272 def __init__(self, git_worktree, src, dest, relsrc, absdest):
273 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 self.src = src
275 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700276 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 self.abs_dest = absdest
278
Wink Saville4c426ef2015-06-03 08:05:17 -0700279 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500280 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700281 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 try:
283 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800284 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800285 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700288 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500289 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700290 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500291 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700292 _error('Cannot link file %s to %s', relSrc, absDest)
293
294 def _Link(self):
295 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
296 on the src linking all of the files in the source in to the destination
297 directory.
298 """
299 # We use the absSrc to handle the situation where the current directory
300 # is not the root of the repo
301 absSrc = os.path.join(self.git_worktree, self.src)
302 if os.path.exists(absSrc):
303 # Entity exists so just a simple one to one link operation
304 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
305 else:
306 # Entity doesn't exist assume there is a wild card
307 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700308 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700309 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700310 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 else:
312 absSrcFiles = glob.glob(absSrc)
313 for absSrcFile in absSrcFiles:
314 # Create a releative path from source dir to destination dir
315 absSrcDir = os.path.dirname(absSrcFile)
316 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
317
318 # Get the source file name
319 srcFile = os.path.basename(absSrcFile)
320
321 # Now form the final full paths to srcFile. They will be
322 # absolute for the desintaiton and relative for the srouce.
323 absDest = os.path.join(absDestDir, srcFile)
324 relSrc = os.path.join(relSrcDir, srcFile)
325 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500326
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700327
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700328class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330 def __init__(self,
331 name,
Anthony King7bdac712014-07-16 12:56:40 +0100332 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700333 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100334 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700335 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700336 orig_name=None,
337 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700338 self.name = name
339 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700340 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700341 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100342 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700343 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700344 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700345
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700346
Doug Anderson37282b42011-03-04 11:54:18 -0800347class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349 """A RepoHook contains information about a script to run as a hook.
350
351 Hooks are used to run a python script before running an upload (for instance,
352 to run presubmit checks). Eventually, we may have hooks for other actions.
353
354 This shouldn't be confused with files in the 'repo/hooks' directory. Those
355 files are copied into each '.git/hooks' folder for each project. Repo-level
356 hooks are associated instead with repo actions.
357
358 Hooks are always python. When a hook is run, we will load the hook into the
359 interpreter and execute its main() function.
360 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700361
Doug Anderson37282b42011-03-04 11:54:18 -0800362 def __init__(self,
363 hook_type,
364 hooks_project,
365 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400366 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800367 abort_if_user_denies=False):
368 """RepoHook constructor.
369
370 Params:
371 hook_type: A string representing the type of hook. This is also used
372 to figure out the name of the file containing the hook. For
373 example: 'pre-upload'.
374 hooks_project: The project containing the repo hooks. If you have a
375 manifest, this is manifest.repo_hooks_project. OK if this is None,
376 which will make the hook a no-op.
377 topdir: Repo's top directory (the one containing the .repo directory).
378 Scripts will run with CWD as this directory. If you have a manifest,
379 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400380 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800381 abort_if_user_denies: If True, we'll throw a HookError() if the user
382 doesn't allow us to run the hook.
383 """
384 self._hook_type = hook_type
385 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400386 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800387 self._topdir = topdir
388 self._abort_if_user_denies = abort_if_user_denies
389
390 # Store the full path to the script for convenience.
391 if self._hooks_project:
392 self._script_fullpath = os.path.join(self._hooks_project.worktree,
393 self._hook_type + '.py')
394 else:
395 self._script_fullpath = None
396
397 def _GetHash(self):
398 """Return a hash of the contents of the hooks directory.
399
400 We'll just use git to do this. This hash has the property that if anything
401 changes in the directory we will return a different has.
402
403 SECURITY CONSIDERATION:
404 This hash only represents the contents of files in the hook directory, not
405 any other files imported or called by hooks. Changes to imported files
406 can change the script behavior without affecting the hash.
407
408 Returns:
409 A string representing the hash. This will always be ASCII so that it can
410 be printed to the user easily.
411 """
412 assert self._hooks_project, "Must have hooks to calculate their hash."
413
414 # We will use the work_git object rather than just calling GetRevisionId().
415 # That gives us a hash of the latest checked in version of the files that
416 # the user will actually be executing. Specifically, GetRevisionId()
417 # doesn't appear to change even if a user checks out a different version
418 # of the hooks repo (via git checkout) nor if a user commits their own revs.
419 #
420 # NOTE: Local (non-committed) changes will not be factored into this hash.
421 # I think this is OK, since we're really only worried about warning the user
422 # about upstream changes.
423 return self._hooks_project.work_git.rev_parse('HEAD')
424
425 def _GetMustVerb(self):
426 """Return 'must' if the hook is required; 'should' if not."""
427 if self._abort_if_user_denies:
428 return 'must'
429 else:
430 return 'should'
431
432 def _CheckForHookApproval(self):
433 """Check to see whether this hook has been approved.
434
Mike Frysinger40252c22016-08-15 21:23:44 -0400435 We'll accept approval of manifest URLs if they're using secure transports.
436 This way the user can say they trust the manifest hoster. For insecure
437 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800438
439 Note that we ask permission for each individual hook even though we use
440 the hash of all hooks when detecting changes. We'd like the user to be
441 able to approve / deny each hook individually. We only use the hash of all
442 hooks because there is no other easy way to detect changes to local imports.
443
444 Returns:
445 True if this hook is approved to run; False otherwise.
446
447 Raises:
448 HookError: Raised if the user doesn't approve and abort_if_user_denies
449 was passed to the consturctor.
450 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400451 if self._ManifestUrlHasSecureScheme():
452 return self._CheckForHookApprovalManifest()
453 else:
454 return self._CheckForHookApprovalHash()
455
456 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
457 changed_prompt):
458 """Check for approval for a particular attribute and hook.
459
460 Args:
461 subkey: The git config key under [repo.hooks.<hook_type>] to store the
462 last approved string.
463 new_val: The new value to compare against the last approved one.
464 main_prompt: Message to display to the user to ask for approval.
465 changed_prompt: Message explaining why we're re-asking for approval.
466
467 Returns:
468 True if this hook is approved to run; False otherwise.
469
470 Raises:
471 HookError: Raised if the user doesn't approve and abort_if_user_denies
472 was passed to the consturctor.
473 """
Doug Anderson37282b42011-03-04 11:54:18 -0800474 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800476
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 # Get the last value that the user approved for this hook; may be None.
478 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800479
Mike Frysinger40252c22016-08-15 21:23:44 -0400480 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800481 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # Approval matched. We're done.
484 return True
485 else:
486 # Give the user a reason why we're prompting, since they last told
487 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400488 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800489 else:
490 prompt = ''
491
492 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
493 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400494 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530495 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900496 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800497
498 # User is doing a one-time approval.
499 if response in ('y', 'yes'):
500 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400501 elif response == 'always':
502 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800503 return True
504
505 # For anything else, we'll assume no approval.
506 if self._abort_if_user_denies:
507 raise HookError('You must allow the %s hook or use --no-verify.' %
508 self._hook_type)
509
510 return False
511
Mike Frysinger40252c22016-08-15 21:23:44 -0400512 def _ManifestUrlHasSecureScheme(self):
513 """Check if the URI for the manifest is a secure transport."""
514 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
515 parse_results = urllib.parse.urlparse(self._manifest_url)
516 return parse_results.scheme in secure_schemes
517
518 def _CheckForHookApprovalManifest(self):
519 """Check whether the user has approved this manifest host.
520
521 Returns:
522 True if this hook is approved to run; False otherwise.
523 """
524 return self._CheckForHookApprovalHelper(
525 'approvedmanifest',
526 self._manifest_url,
527 'Run hook scripts from %s' % (self._manifest_url,),
528 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
529
530 def _CheckForHookApprovalHash(self):
531 """Check whether the user has approved the hooks repo.
532
533 Returns:
534 True if this hook is approved to run; False otherwise.
535 """
536 prompt = ('Repo %s run the script:\n'
537 ' %s\n'
538 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700539 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400540 return self._CheckForHookApprovalHelper(
541 'approvedhash',
542 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700543 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400544 'Scripts have changed since %s was allowed.' % (self._hook_type,))
545
Doug Anderson37282b42011-03-04 11:54:18 -0800546 def _ExecuteHook(self, **kwargs):
547 """Actually execute the given hook.
548
549 This will run the hook's 'main' function in our python interpreter.
550
551 Args:
552 kwargs: Keyword arguments to pass to the hook. These are often specific
553 to the hook type. For instance, pre-upload hooks will contain
554 a project_list.
555 """
556 # Keep sys.path and CWD stashed away so that we can always restore them
557 # upon function exit.
558 orig_path = os.getcwd()
559 orig_syspath = sys.path
560
561 try:
562 # Always run hooks with CWD as topdir.
563 os.chdir(self._topdir)
564
565 # Put the hook dir as the first item of sys.path so hooks can do
566 # relative imports. We want to replace the repo dir as [0] so
567 # hooks can't import repo files.
568 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
569
570 # Exec, storing global context in the context dict. We catch exceptions
571 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500572 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800573 try:
Anthony King70f68902014-05-05 21:15:34 +0100574 exec(compile(open(self._script_fullpath).read(),
575 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800576 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700577 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
578 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800579
580 # Running the script should have defined a main() function.
581 if 'main' not in context:
582 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
583
Doug Anderson37282b42011-03-04 11:54:18 -0800584 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
585 # We don't actually want hooks to define their main with this argument--
586 # it's there to remind them that their hook should always take **kwargs.
587 # For instance, a pre-upload hook should be defined like:
588 # def main(project_list, **kwargs):
589 #
590 # This allows us to later expand the API without breaking old hooks.
591 kwargs = kwargs.copy()
592 kwargs['hook_should_take_kwargs'] = True
593
594 # Call the main function in the hook. If the hook should cause the
595 # build to fail, it will raise an Exception. We'll catch that convert
596 # to a HookError w/ just the failing traceback.
597 try:
598 context['main'](**kwargs)
599 except Exception:
600 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700601 'above.' % (traceback.format_exc(),
602 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800603 finally:
604 # Restore sys.path and CWD.
605 sys.path = orig_syspath
606 os.chdir(orig_path)
607
608 def Run(self, user_allows_all_hooks, **kwargs):
609 """Run the hook.
610
611 If the hook doesn't exist (because there is no hooks project or because
612 this particular hook is not enabled), this is a no-op.
613
614 Args:
615 user_allows_all_hooks: If True, we will never prompt about running the
616 hook--we'll just assume it's OK to run it.
617 kwargs: Keyword arguments to pass to the hook. These are often specific
618 to the hook type. For instance, pre-upload hooks will contain
619 a project_list.
620
621 Raises:
622 HookError: If there was a problem finding the hook or the user declined
623 to run a required hook (from _CheckForHookApproval).
624 """
625 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700626 if ((not self._hooks_project) or (self._hook_type not in
627 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800628 return
629
630 # Bail with a nice error if we can't find the hook.
631 if not os.path.isfile(self._script_fullpath):
632 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
633
634 # Make sure the user is OK with running the hook.
635 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
636 return
637
638 # Run the hook with the same version of python we're using.
639 self._ExecuteHook(**kwargs)
640
641
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600643 # These objects can be shared between several working trees.
644 shareable_files = ['description', 'info']
645 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
646 # These objects can only be used by a single working tree.
647 working_tree_files = ['config', 'packed-refs', 'shallow']
648 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700649
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650 def __init__(self,
651 manifest,
652 name,
653 remote,
654 gitdir,
David James8d201162013-10-11 17:03:19 -0700655 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 worktree,
657 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700658 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800659 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100660 rebase=True,
661 groups=None,
662 sync_c=False,
663 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900664 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100665 clone_depth=None,
666 upstream=None,
667 parent=None,
668 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900669 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700670 optimized_fetch=False,
671 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800672 """Init a Project object.
673
674 Args:
675 manifest: The XmlManifest object.
676 name: The `name` attribute of manifest.xml's project element.
677 remote: RemoteSpec object specifying its remote's properties.
678 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700679 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800680 worktree: Absolute path of git working tree.
681 relpath: Relative path of git working tree to repo's top directory.
682 revisionExpr: The `revision` attribute of manifest.xml's project element.
683 revisionId: git commit id for checking out.
684 rebase: The `rebase` attribute of manifest.xml's project element.
685 groups: The `groups` attribute of manifest.xml's project element.
686 sync_c: The `sync-c` attribute of manifest.xml's project element.
687 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900688 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800689 upstream: The `upstream` attribute of manifest.xml's project element.
690 parent: The parent Project object.
691 is_derived: False if the project was explicitly defined in the manifest;
692 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400693 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900694 optimized_fetch: If True, when a project is set to a sha1 revision, only
695 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700696 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800697 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698 self.manifest = manifest
699 self.name = name
700 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800701 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700702 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800703 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700704 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800705 else:
706 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700708 self.revisionExpr = revisionExpr
709
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700710 if revisionId is None \
711 and revisionExpr \
712 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700713 self.revisionId = revisionExpr
714 else:
715 self.revisionId = revisionId
716
Mike Pontillod3153822012-02-28 11:53:24 -0800717 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700718 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700719 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800720 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900721 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900722 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700723 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 self.parent = parent
725 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900726 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800727 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800728
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700729 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500731 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500732 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700733 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
734 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800736 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700737 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800738 else:
739 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700740 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700741 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700742 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400743 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700744 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745
Doug Anderson37282b42011-03-04 11:54:18 -0800746 # This will be filled in if a project is later identified to be the
747 # project containing repo hooks.
748 self.enabled_repo_hooks = []
749
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700750 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800751 def Derived(self):
752 return self.is_derived
753
754 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700756 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757
758 @property
759 def CurrentBranch(self):
760 """Obtain the name of the currently checked out branch.
761 The branch name omits the 'refs/heads/' prefix.
762 None is returned if the project is on a detached HEAD.
763 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700764 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 if b.startswith(R_HEADS):
766 return b[len(R_HEADS):]
767 return None
768
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700769 def IsRebaseInProgress(self):
770 w = self.worktree
771 g = os.path.join(w, '.git')
772 return os.path.exists(os.path.join(g, 'rebase-apply')) \
773 or os.path.exists(os.path.join(g, 'rebase-merge')) \
774 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200775
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 def IsDirty(self, consider_untracked=True):
777 """Is the working directory modified in some way?
778 """
779 self.work_git.update_index('-q',
780 '--unmerged',
781 '--ignore-missing',
782 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900783 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784 return True
785 if self.work_git.DiffZ('diff-files'):
786 return True
787 if consider_untracked and self.work_git.LsOthers():
788 return True
789 return False
790
791 _userident_name = None
792 _userident_email = None
793
794 @property
795 def UserName(self):
796 """Obtain the user's personal name.
797 """
798 if self._userident_name is None:
799 self._LoadUserIdentity()
800 return self._userident_name
801
802 @property
803 def UserEmail(self):
804 """Obtain the user's email address. This is very likely
805 to be their Gerrit login.
806 """
807 if self._userident_email is None:
808 self._LoadUserIdentity()
809 return self._userident_email
810
811 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900812 u = self.bare_git.var('GIT_COMMITTER_IDENT')
813 m = re.compile("^(.*) <([^>]*)> ").match(u)
814 if m:
815 self._userident_name = m.group(1)
816 self._userident_email = m.group(2)
817 else:
818 self._userident_name = ''
819 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
821 def GetRemote(self, name):
822 """Get the configuration for a single remote.
823 """
824 return self.config.GetRemote(name)
825
826 def GetBranch(self, name):
827 """Get the configuration for a single branch.
828 """
829 return self.config.GetBranch(name)
830
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 def GetBranches(self):
832 """Get all existing local branches.
833 """
834 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900835 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530838 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700839 if name.startswith(R_HEADS):
840 name = name[len(R_HEADS):]
841 b = self.GetBranch(name)
842 b.current = name == current
843 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845 heads[name] = b
846
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530847 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700848 if name.startswith(R_PUB):
849 name = name[len(R_PUB):]
850 b = heads.get(name)
851 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900852 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700853
854 return heads
855
Colin Cross5acde752012-03-28 20:15:45 -0700856 def MatchesGroups(self, manifest_groups):
857 """Returns true if the manifest groups specified at init should cause
858 this project to be synced.
859 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700861
862 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700864 manifest_groups: "-group1,group2"
865 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500866
867 The special manifest group "default" will match any project that
868 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700869 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500870 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700872 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500873 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700874
Conley Owens971de8e2012-04-16 10:36:08 -0700875 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876 for group in expanded_manifest_groups:
877 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700878 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700879 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700880 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700881
Conley Owens971de8e2012-04-16 10:36:08 -0700882 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700884# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 def UncommitedFiles(self, get_all=True):
886 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700888 Args:
889 get_all: a boolean, if True - get information about all different
890 uncommitted files. If False - return as soon as any kind of
891 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500892 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700893 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894 self.work_git.update_index('-q',
895 '--unmerged',
896 '--ignore-missing',
897 '--refresh')
898 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700899 details.append("rebase in progress")
900 if not get_all:
901 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500902
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700903 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
904 if changes:
905 details.extend(changes)
906 if not get_all:
907 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500908
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700909 changes = self.work_git.DiffZ('diff-files').keys()
910 if changes:
911 details.extend(changes)
912 if not get_all:
913 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500914
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700915 changes = self.work_git.LsOthers()
916 if changes:
917 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500918
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700919 return details
920
921 def HasChanges(self):
922 """Returns true if there are uncommitted changes.
923 """
924 if self.UncommitedFiles(get_all=False):
925 return True
926 else:
927 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500928
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600929 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200931
932 Args:
933 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600934 quiet: If True then only print the project name. Do not print
935 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700937 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700938 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200939 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700940 print(file=output_redir)
941 print('project %s/' % self.relpath, file=output_redir)
942 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700943 return
944
945 self.work_git.update_index('-q',
946 '--unmerged',
947 '--ignore-missing',
948 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700949 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
951 df = self.work_git.DiffZ('diff-files')
952 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100953 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700954 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
956 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700957 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200958 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700959 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700960
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600961 if quiet:
962 out.nl()
963 return 'DIRTY'
964
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700965 branch = self.CurrentBranch
966 if branch is None:
967 out.nobranch('(*** NO BRANCH ***)')
968 else:
969 out.branch('branch %s', branch)
970 out.nl()
971
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700972 if rb:
973 out.important('prior sync failed; rebase still in progress')
974 out.nl()
975
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976 paths = list()
977 paths.extend(di.keys())
978 paths.extend(df.keys())
979 paths.extend(do)
980
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530981 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900982 try:
983 i = di[p]
984 except KeyError:
985 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900987 try:
988 f = df[p]
989 except KeyError:
990 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200991
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900992 if i:
993 i_status = i.status.upper()
994 else:
995 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700996
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900997 if f:
998 f_status = f.status.lower()
999 else:
1000 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
1002 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001003 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001004 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005 else:
1006 line = ' %s%s\t%s' % (i_status, f_status, p)
1007
1008 if i and not f:
1009 out.added('%s', line)
1010 elif (i and f) or (not i and f):
1011 out.changed('%s', line)
1012 elif not i and not f:
1013 out.untracked('%s', line)
1014 else:
1015 out.write('%s', line)
1016 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001017
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001018 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001019
pelyad67872d2012-03-28 14:49:58 +03001020 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 """Prints the status of the repository to stdout.
1022 """
1023 out = DiffColoring(self.config)
1024 cmd = ['diff']
1025 if out.is_on:
1026 cmd.append('--color')
1027 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001028 if absolute_paths:
1029 cmd.append('--src-prefix=a/%s/' % self.relpath)
1030 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 cmd.append('--')
1032 p = GitCommand(self,
1033 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001034 capture_stdout=True,
1035 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 has_diff = False
1037 for line in p.process.stdout:
1038 if not has_diff:
1039 out.nl()
1040 out.project('project %s/' % self.relpath)
1041 out.nl()
1042 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001043 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044 p.Wait()
1045
1046
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001047# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
David Pursehouse8a68ff92012-09-24 12:15:13 +09001049 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 """Was the branch published (uploaded) for code review?
1051 If so, returns the SHA-1 hash of the last published
1052 state for the branch.
1053 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001054 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001055 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001056 try:
1057 return self.bare_git.rev_parse(key)
1058 except GitError:
1059 return None
1060 else:
1061 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001062 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001063 except KeyError:
1064 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 """Prunes any stale published refs.
1068 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 if all_refs is None:
1070 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 heads = set()
1072 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301073 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 if name.startswith(R_HEADS):
1075 heads.add(name)
1076 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001077 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301079 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080 n = name[len(R_PUB):]
1081 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001082 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001084 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 """List any branches which can be uploaded for review.
1086 """
1087 heads = {}
1088 pubed = {}
1089
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301090 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001092 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001094 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095
1096 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301097 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001098 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001099 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001100 if selected_branch and branch != selected_branch:
1101 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001103 rb = self.GetUploadableBranch(branch)
1104 if rb:
1105 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001106 return ready
1107
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001108 def GetUploadableBranch(self, branch_name):
1109 """Get a single uploadable branch, or None.
1110 """
1111 branch = self.GetBranch(branch_name)
1112 base = branch.LocalMerge
1113 if branch.LocalMerge:
1114 rb = ReviewableBranch(self, branch, base)
1115 if rb.commits:
1116 return rb
1117 return None
1118
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001119 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001120 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001121 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001122 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001123 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001124 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001125 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001126 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001127 validate_certs=True,
1128 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001129 """Uploads the named branch for code review.
1130 """
1131 if branch is None:
1132 branch = self.CurrentBranch
1133 if branch is None:
1134 raise GitError('not currently on a branch')
1135
1136 branch = self.GetBranch(branch)
1137 if not branch.LocalMerge:
1138 raise GitError('branch %s does not track a remote' % branch.name)
1139 if not branch.remote.review:
1140 raise GitError('remote %s has no review url' % branch.remote.name)
1141
Bryan Jacobsf609f912013-05-06 13:36:24 -04001142 if dest_branch is None:
1143 dest_branch = self.dest_branch
1144 if dest_branch is None:
1145 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001146 if not dest_branch.startswith(R_HEADS):
1147 dest_branch = R_HEADS + dest_branch
1148
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001149 if not branch.remote.projectname:
1150 branch.remote.projectname = self.name
1151 branch.remote.Save()
1152
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001153 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001154 if url is None:
1155 raise UploadError('review not configured')
1156 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001157
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001158 if url.startswith('ssh://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001159 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001160
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001161 for push_option in (push_options or []):
1162 cmd.append('-o')
1163 cmd.append(push_option)
1164
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001165 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001166
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 if dest_branch.startswith(R_HEADS):
1168 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001169
1170 upload_type = 'for'
1171 if draft:
1172 upload_type = 'drafts'
1173
1174 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1175 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001176 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001177 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001178 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001179
David Pursehousef25a3702018-11-14 19:01:22 -08001180 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001181 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001182 if notify:
1183 opts += ['notify=' + notify]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001184 if private:
1185 opts += ['private']
1186 if wip:
1187 opts += ['wip']
1188 if opts:
1189 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001190 cmd.append(ref_spec)
1191
Anthony King7bdac712014-07-16 12:56:40 +01001192 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001193 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001194
1195 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1196 self.bare_git.UpdateRef(R_PUB + branch.name,
1197 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001198 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001199
1200
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001201# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001202
Julien Campergue335f5ef2013-10-16 11:02:35 +02001203 def _ExtractArchive(self, tarpath, path=None):
1204 """Extract the given tar on its current location
1205
1206 Args:
1207 - tarpath: The path to the actual tar file
1208
1209 """
1210 try:
1211 with tarfile.open(tarpath, 'r') as tar:
1212 tar.extractall(path=path)
1213 return True
1214 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001215 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001216 return False
1217
Ningning Xiac2fbc782016-08-22 14:24:39 -07001218 def CachePopulate(self, cache_dir, url):
1219 """Populate cache in the cache_dir.
1220
1221 Args:
1222 cache_dir: Directory to cache git files from Google Storage.
1223 url: Git url of current repository.
1224
1225 Raises:
1226 CacheApplyError if it fails to populate the git cache.
1227 """
1228 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1229 '--cache-dir', cache_dir, url]
1230
1231 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1232 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1233 'url: %s' % (cache_dir, url))
1234
1235 def CacheExists(self, cache_dir, url):
1236 """Check the existence of the cache files.
1237
1238 Args:
1239 cache_dir: Directory to cache git files.
1240 url: Git url of current repository.
1241
1242 Raises:
1243 CacheApplyError if the cache files do not exist.
1244 """
1245 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1246
1247 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1248 if exist.Wait() != 0:
1249 raise CacheApplyError('Failed to execute git cache exists cmd. '
1250 'cache_dir: %s url: %s' % (cache_dir, url))
1251
1252 if not exist.stdout or not exist.stdout.strip():
1253 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1254 'url: %s' % (cache_dir, url))
1255 return exist.stdout.strip()
1256
1257 def CacheApply(self, cache_dir):
1258 """Apply git cache files populated from Google Storage buckets.
1259
1260 Args:
1261 cache_dir: Directory to cache git files.
1262
1263 Raises:
1264 CacheApplyError if it fails to apply git caches.
1265 """
1266 remote = self.GetRemote(self.remote.name)
1267
1268 self.CachePopulate(cache_dir, remote.url)
1269
1270 mirror_dir = self.CacheExists(cache_dir, remote.url)
1271
1272 refspec = RefSpec(True, 'refs/heads/*',
1273 'refs/remotes/%s/*' % remote.name)
1274
1275 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1276 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1277 raise CacheApplyError('Failed to fetch refs %s from %s' %
1278 (mirror_dir, str(refspec)))
1279
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001280 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001281 quiet=False,
1282 is_new=None,
1283 current_branch_only=False,
1284 force_sync=False,
1285 clone_bundle=True,
1286 no_tags=False,
1287 archive=False,
1288 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001289 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001290 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001291 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001292 """Perform only the network IO portion of the sync process.
1293 Local working directory/branch state is not affected.
1294 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001295 if archive and not isinstance(self, MetaProject):
1296 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001297 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001298 return False
1299
1300 name = self.relpath.replace('\\', '/')
1301 name = name.replace('/', '_')
1302 tarpath = '%s.tar' % name
1303 topdir = self.manifest.topdir
1304
1305 try:
1306 self._FetchArchive(tarpath, cwd=topdir)
1307 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001308 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001309 return False
1310
1311 # From now on, we only need absolute tarpath
1312 tarpath = os.path.join(topdir, tarpath)
1313
1314 if not self._ExtractArchive(tarpath, path=topdir):
1315 return False
1316 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001317 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001318 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001319 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001320 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001321 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001322 if is_new is None:
1323 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001324 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001325 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001326 else:
1327 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001328 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001329
1330 if is_new:
1331 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1332 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001333 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001334 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001335 # This works for both absolute and relative alternate directories.
1336 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001337 finally:
1338 fd.close()
1339 except IOError:
1340 alt_dir = None
1341 else:
1342 alt_dir = None
1343
Ningning Xiac2fbc782016-08-22 14:24:39 -07001344 applied_cache = False
1345 # If cache_dir is provided, and it's a new repository without
1346 # alternative_dir, bootstrap this project repo with the git
1347 # cache files.
1348 if cache_dir is not None and is_new and alt_dir is None:
1349 try:
1350 self.CacheApply(cache_dir)
1351 applied_cache = True
1352 is_new = False
1353 except CacheApplyError as e:
1354 _error('Could not apply git cache: %s', e)
1355 _error('Please check if you have the right GS credentials.')
1356 _error('Please check if the cache files exist in GS.')
1357
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001358 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001359 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001360 and alt_dir is None \
1361 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001362 is_new = False
1363
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001364 if not current_branch_only:
1365 if self.sync_c:
1366 current_branch_only = True
1367 elif not self.manifest._loaded:
1368 # Manifest cannot check defaults until it syncs.
1369 current_branch_only = False
1370 elif self.manifest.default.sync_c:
1371 current_branch_only = True
1372
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001373 if not no_tags:
1374 if not self.sync_tags:
1375 no_tags = True
1376
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001377 if self.clone_depth:
1378 depth = self.clone_depth
1379 else:
1380 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1381
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001382 need_to_fetch = not (optimized_fetch and
1383 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001384 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001385 if (need_to_fetch and
1386 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1387 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001388 no_tags=no_tags, prune=prune, depth=depth,
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04001389 submodules=submodules, force_sync=force_sync)):
Anthony King7bdac712014-07-16 12:56:40 +01001390 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001391
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001392 mp = self.manifest.manifestProject
1393 dissociate = mp.config.GetBoolean('repo.dissociate')
1394 if dissociate:
1395 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1396 if os.path.exists(alternates_file):
1397 cmd = ['repack', '-a', '-d']
1398 if GitCommand(self, cmd, bare=True).Wait() != 0:
1399 return False
1400 platform_utils.remove(alternates_file)
1401
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001402 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001403 self._InitMRef()
1404 else:
1405 self._InitMirrorHead()
1406 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001407 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001408 except OSError:
1409 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001410 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001411
1412 def PostRepoUpgrade(self):
1413 self._InitHooks()
1414
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001415 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001416 if self.manifest.isGitcClient:
1417 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001418 for copyfile in self.copyfiles:
1419 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001420 for linkfile in self.linkfiles:
1421 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001422
Julien Camperguedd654222014-01-09 16:21:37 +01001423 def GetCommitRevisionId(self):
1424 """Get revisionId of a commit.
1425
1426 Use this method instead of GetRevisionId to get the id of the commit rather
1427 than the id of the current git object (for example, a tag)
1428
1429 """
1430 if not self.revisionExpr.startswith(R_TAGS):
1431 return self.GetRevisionId(self._allrefs)
1432
1433 try:
1434 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1435 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001436 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1437 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001438
David Pursehouse8a68ff92012-09-24 12:15:13 +09001439 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001440 if self.revisionId:
1441 return self.revisionId
1442
1443 rem = self.GetRemote(self.remote.name)
1444 rev = rem.ToLocal(self.revisionExpr)
1445
David Pursehouse8a68ff92012-09-24 12:15:13 +09001446 if all_refs is not None and rev in all_refs:
1447 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001448
1449 try:
1450 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1451 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001452 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1453 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001454
Martin Kellye4e94d22017-03-21 16:05:12 -07001455 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001456 """Perform only the local IO portion of the sync process.
1457 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001458 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001459 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001460 all_refs = self.bare_ref.all
1461 self.CleanPublishedCache(all_refs)
1462 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001463
David Pursehouse1d947b32012-10-25 12:23:11 +09001464 def _doff():
1465 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001466 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001467
Martin Kellye4e94d22017-03-21 16:05:12 -07001468 def _dosubmodules():
1469 self._SyncSubmodules(quiet=True)
1470
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001471 head = self.work_git.GetHead()
1472 if head.startswith(R_HEADS):
1473 branch = head[len(R_HEADS):]
1474 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001475 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001476 except KeyError:
1477 head = None
1478 else:
1479 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001480
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001481 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001482 # Currently on a detached HEAD. The user is assumed to
1483 # not have any local modifications worth worrying about.
1484 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001485 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001486 syncbuf.fail(self, _PriorSyncFailedError())
1487 return
1488
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001489 if head == revid:
1490 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001491 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001492 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001493 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001494 # The copy/linkfile config may have changed.
1495 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001496 return
1497 else:
1498 lost = self._revlist(not_rev(revid), HEAD)
1499 if lost:
1500 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001501
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001503 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001504 if submodules:
1505 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001506 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001507 syncbuf.fail(self, e)
1508 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001509 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001510 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001511
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001512 if head == revid:
1513 # No changes; don't do anything further.
1514 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001515 # The copy/linkfile config may have changed.
1516 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001517 return
1518
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001520
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001521 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001522 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001523 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001525 syncbuf.info(self,
1526 "leaving %s; does not track upstream",
1527 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001528 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001529 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001530 if submodules:
1531 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001532 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001533 syncbuf.fail(self, e)
1534 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001535 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001536 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001537
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001538 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001539 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001540 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001541 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001542 if not_merged:
1543 if upstream_gain:
1544 # The user has published this branch and some of those
1545 # commits are not yet merged upstream. We do not want
1546 # to rewrite the published commits so we punt.
1547 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001548 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001549 "branch %s is published (but not merged) and is now "
1550 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001551 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001552 elif pub == head:
1553 # All published commits are merged, and thus we are a
1554 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001555 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001556 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001557 if submodules:
1558 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001559 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001560
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001561 # Examine the local commits not in the remote. Find the
1562 # last one attributed to this user, if any.
1563 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001564 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001565 last_mine = None
1566 cnt_mine = 0
1567 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301568 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001569 if committer_email == self.UserEmail:
1570 last_mine = commit_id
1571 cnt_mine += 1
1572
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001573 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001574 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001575
1576 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001577 syncbuf.fail(self, _DirtyError())
1578 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001579
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001580 # If the upstream switched on us, warn the user.
1581 #
1582 if branch.merge != self.revisionExpr:
1583 if branch.merge and self.revisionExpr:
1584 syncbuf.info(self,
1585 'manifest switched %s...%s',
1586 branch.merge,
1587 self.revisionExpr)
1588 elif branch.merge:
1589 syncbuf.info(self,
1590 'manifest no longer tracks %s',
1591 branch.merge)
1592
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001593 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001594 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001595 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001596 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001597 syncbuf.info(self,
1598 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001599 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001600
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001601 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001602 if not ID_RE.match(self.revisionExpr):
1603 # in case of manifest sync the revisionExpr might be a SHA1
1604 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001605 if not branch.merge.startswith('refs/'):
1606 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001607 branch.Save()
1608
Mike Pontillod3153822012-02-28 11:53:24 -08001609 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001610 def _docopyandlink():
1611 self._CopyAndLinkFiles()
1612
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001613 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001614 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001615 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001616 if submodules:
1617 syncbuf.later2(self, _dosubmodules)
1618 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001619 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001620 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001621 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001622 if submodules:
1623 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001624 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001625 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001626 syncbuf.fail(self, e)
1627 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001628 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001629 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001630 if submodules:
1631 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001632
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001633 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001634 # dest should already be an absolute path, but src is project relative
1635 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001636 abssrc = os.path.join(self.worktree, src)
1637 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001639 def AddLinkFile(self, src, dest, absdest):
1640 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001641 # make src relative path to dest
1642 absdestdir = os.path.dirname(absdest)
1643 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001644 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001645
James W. Mills24c13082012-04-12 15:04:13 -05001646 def AddAnnotation(self, name, value, keep):
1647 self.annotations.append(_Annotation(name, value, keep))
1648
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001649 def DownloadPatchSet(self, change_id, patch_id):
1650 """Download a single patch set of a single change to FETCH_HEAD.
1651 """
1652 remote = self.GetRemote(self.remote.name)
1653
1654 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001655 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001656 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001657 if GitCommand(self, cmd, bare=True).Wait() != 0:
1658 return None
1659 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001660 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001661 change_id,
1662 patch_id,
1663 self.bare_git.rev_parse('FETCH_HEAD'))
1664
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001665
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001666# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001667
Simran Basib9a1b732015-08-20 12:19:28 -07001668 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001669 """Create a new branch off the manifest's revision.
1670 """
Simran Basib9a1b732015-08-20 12:19:28 -07001671 if not branch_merge:
1672 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001673 head = self.work_git.GetHead()
1674 if head == (R_HEADS + name):
1675 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001676
David Pursehouse8a68ff92012-09-24 12:15:13 +09001677 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001678 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001679 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001680 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001681 capture_stdout=True,
1682 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001683
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001684 branch = self.GetBranch(name)
1685 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001686 branch.merge = branch_merge
1687 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1688 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001689 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001690
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001691 if head.startswith(R_HEADS):
1692 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001693 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001694 except KeyError:
1695 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001696 if revid and head and revid == head:
1697 ref = os.path.join(self.gitdir, R_HEADS + name)
1698 try:
1699 os.makedirs(os.path.dirname(ref))
1700 except OSError:
1701 pass
1702 _lwrite(ref, '%s\n' % revid)
1703 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1704 'ref: %s%s\n' % (R_HEADS, name))
1705 branch.Save()
1706 return True
1707
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001708 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001709 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001710 capture_stdout=True,
1711 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001712 branch.Save()
1713 return True
1714 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001715
Wink Saville02d79452009-04-10 13:01:24 -07001716 def CheckoutBranch(self, name):
1717 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001718
1719 Args:
1720 name: The name of the branch to checkout.
1721
1722 Returns:
1723 True if the checkout succeeded; False if it didn't; None if the branch
1724 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001725 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001726 rev = R_HEADS + name
1727 head = self.work_git.GetHead()
1728 if head == rev:
1729 # Already on the branch
1730 #
1731 return True
Wink Saville02d79452009-04-10 13:01:24 -07001732
David Pursehouse8a68ff92012-09-24 12:15:13 +09001733 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001734 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001735 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001736 except KeyError:
1737 # Branch does not exist in this project
1738 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001739 return None
Wink Saville02d79452009-04-10 13:01:24 -07001740
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001741 if head.startswith(R_HEADS):
1742 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001743 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001744 except KeyError:
1745 head = None
1746
1747 if head == revid:
1748 # Same revision; just update HEAD to point to the new
1749 # target branch, but otherwise take no other action.
1750 #
1751 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1752 'ref: %s%s\n' % (R_HEADS, name))
1753 return True
1754
1755 return GitCommand(self,
1756 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001757 capture_stdout=True,
1758 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001759
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001760 def AbandonBranch(self, name):
1761 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001762
1763 Args:
1764 name: The name of the branch to abandon.
1765
1766 Returns:
1767 True if the abandon succeeded; False if it didn't; None if the branch
1768 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001769 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001770 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001771 all_refs = self.bare_ref.all
1772 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001773 # Doesn't exist
1774 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001775
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001776 head = self.work_git.GetHead()
1777 if head == rev:
1778 # We can't destroy the branch while we are sitting
1779 # on it. Switch to a detached HEAD.
1780 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001781 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001782
David Pursehouse8a68ff92012-09-24 12:15:13 +09001783 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001784 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001785 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1786 '%s\n' % revid)
1787 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001788 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001789
1790 return GitCommand(self,
1791 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001792 capture_stdout=True,
1793 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001794
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001795 def PruneHeads(self):
1796 """Prune any topic branches already merged into upstream.
1797 """
1798 cb = self.CurrentBranch
1799 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001800 left = self._allrefs
1801 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001802 if name.startswith(R_HEADS):
1803 name = name[len(R_HEADS):]
1804 if cb is None or name != cb:
1805 kill.append(name)
1806
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001807 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001808 if cb is not None \
1809 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001810 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001811 self.work_git.DetachHead(HEAD)
1812 kill.append(cb)
1813
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001814 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001815 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001816
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001817 try:
1818 self.bare_git.DetachHead(rev)
1819
1820 b = ['branch', '-d']
1821 b.extend(kill)
1822 b = GitCommand(self, b, bare=True,
1823 capture_stdout=True,
1824 capture_stderr=True)
1825 b.Wait()
1826 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001827 if ID_RE.match(old):
1828 self.bare_git.DetachHead(old)
1829 else:
1830 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001831 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001832
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001833 for branch in kill:
1834 if (R_HEADS + branch) not in left:
1835 self.CleanPublishedCache()
1836 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001837
1838 if cb and cb not in kill:
1839 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001840 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001841
1842 kept = []
1843 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001844 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001845 branch = self.GetBranch(branch)
1846 base = branch.LocalMerge
1847 if not base:
1848 base = rev
1849 kept.append(ReviewableBranch(self, branch, base))
1850 return kept
1851
1852
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001853# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001854
1855 def GetRegisteredSubprojects(self):
1856 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001857
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001858 def rec(subprojects):
1859 if not subprojects:
1860 return
1861 result.extend(subprojects)
1862 for p in subprojects:
1863 rec(p.subprojects)
1864 rec(self.subprojects)
1865 return result
1866
1867 def _GetSubmodules(self):
1868 # Unfortunately we cannot call `git submodule status --recursive` here
1869 # because the working tree might not exist yet, and it cannot be used
1870 # without a working tree in its current implementation.
1871
1872 def get_submodules(gitdir, rev):
1873 # Parse .gitmodules for submodule sub_paths and sub_urls
1874 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1875 if not sub_paths:
1876 return []
1877 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1878 # revision of submodule repository
1879 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1880 submodules = []
1881 for sub_path, sub_url in zip(sub_paths, sub_urls):
1882 try:
1883 sub_rev = sub_revs[sub_path]
1884 except KeyError:
1885 # Ignore non-exist submodules
1886 continue
1887 submodules.append((sub_rev, sub_path, sub_url))
1888 return submodules
1889
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001890 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1891 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001892
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001893 def parse_gitmodules(gitdir, rev):
1894 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1895 try:
Anthony King7bdac712014-07-16 12:56:40 +01001896 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1897 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001898 except GitError:
1899 return [], []
1900 if p.Wait() != 0:
1901 return [], []
1902
1903 gitmodules_lines = []
1904 fd, temp_gitmodules_path = tempfile.mkstemp()
1905 try:
1906 os.write(fd, p.stdout)
1907 os.close(fd)
1908 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001909 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1910 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001911 if p.Wait() != 0:
1912 return [], []
1913 gitmodules_lines = p.stdout.split('\n')
1914 except GitError:
1915 return [], []
1916 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001917 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001918
1919 names = set()
1920 paths = {}
1921 urls = {}
1922 for line in gitmodules_lines:
1923 if not line:
1924 continue
1925 m = re_path.match(line)
1926 if m:
1927 names.add(m.group(1))
1928 paths[m.group(1)] = m.group(2)
1929 continue
1930 m = re_url.match(line)
1931 if m:
1932 names.add(m.group(1))
1933 urls[m.group(1)] = m.group(2)
1934 continue
1935 names = sorted(names)
1936 return ([paths.get(name, '') for name in names],
1937 [urls.get(name, '') for name in names])
1938
1939 def git_ls_tree(gitdir, rev, paths):
1940 cmd = ['ls-tree', rev, '--']
1941 cmd.extend(paths)
1942 try:
Anthony King7bdac712014-07-16 12:56:40 +01001943 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1944 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001945 except GitError:
1946 return []
1947 if p.Wait() != 0:
1948 return []
1949 objects = {}
1950 for line in p.stdout.split('\n'):
1951 if not line.strip():
1952 continue
1953 object_rev, object_path = line.split()[2:4]
1954 objects[object_path] = object_rev
1955 return objects
1956
1957 try:
1958 rev = self.GetRevisionId()
1959 except GitError:
1960 return []
1961 return get_submodules(self.gitdir, rev)
1962
1963 def GetDerivedSubprojects(self):
1964 result = []
1965 if not self.Exists:
1966 # If git repo does not exist yet, querying its submodules will
1967 # mess up its states; so return here.
1968 return result
1969 for rev, path, url in self._GetSubmodules():
1970 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001971 relpath, worktree, gitdir, objdir = \
1972 self.manifest.GetSubprojectPaths(self, name, path)
1973 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001974 if project:
1975 result.extend(project.GetDerivedSubprojects())
1976 continue
David James8d201162013-10-11 17:03:19 -07001977
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001978 if url.startswith('..'):
1979 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001980 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001981 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001982 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001983 review=self.remote.review,
1984 revision=self.remote.revision)
1985 subproject = Project(manifest=self.manifest,
1986 name=name,
1987 remote=remote,
1988 gitdir=gitdir,
1989 objdir=objdir,
1990 worktree=worktree,
1991 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001992 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001993 revisionId=rev,
1994 rebase=self.rebase,
1995 groups=self.groups,
1996 sync_c=self.sync_c,
1997 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001998 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001999 parent=self,
2000 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002001 result.append(subproject)
2002 result.extend(subproject.GetDerivedSubprojects())
2003 return result
2004
2005
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002006# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002007 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002008 try:
2009 # if revision (sha or tag) is not present then following function
2010 # throws an error.
2011 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2012 return True
2013 except GitError:
2014 # There is no such persistent revision. We have to fetch it.
2015 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002016
Julien Campergue335f5ef2013-10-16 11:02:35 +02002017 def _FetchArchive(self, tarpath, cwd=None):
2018 cmd = ['archive', '-v', '-o', tarpath]
2019 cmd.append('--remote=%s' % self.remote.url)
2020 cmd.append('--prefix=%s/' % self.relpath)
2021 cmd.append(self.revisionExpr)
2022
2023 command = GitCommand(self, cmd, cwd=cwd,
2024 capture_stdout=True,
2025 capture_stderr=True)
2026
2027 if command.Wait() != 0:
2028 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2029
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002030 def _RemoteFetch(self, name=None,
2031 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002032 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002033 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002034 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002035 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002036 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002037 depth=None,
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002038 submodules=False,
2039 force_sync=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002040
2041 is_sha1 = False
2042 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002043 # The depth should not be used when fetching to a mirror because
2044 # it will result in a shallow repository that cannot be cloned or
2045 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002046 # The repo project should also never be synced with partial depth.
2047 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2048 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002049
Shawn Pearce69e04d82014-01-29 12:48:54 -08002050 if depth:
2051 current_branch_only = True
2052
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002053 if ID_RE.match(self.revisionExpr) is not None:
2054 is_sha1 = True
2055
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002056 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002057 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002058 # this is a tag and its sha1 value should never change
2059 tag_name = self.revisionExpr[len(R_TAGS):]
2060
2061 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002062 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002063 if not quiet:
2064 print('Skipped fetching project %s (already have persistent ref)'
2065 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002066 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002067 if is_sha1 and not depth:
2068 # When syncing a specific commit and --depth is not set:
2069 # * if upstream is explicitly specified and is not a sha1, fetch only
2070 # upstream as users expect only upstream to be fetch.
2071 # Note: The commit might not be in upstream in which case the sync
2072 # will fail.
2073 # * otherwise, fetch all branches to make sure we end up with the
2074 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002075 if self.upstream:
2076 current_branch_only = not ID_RE.match(self.upstream)
2077 else:
2078 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002079
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002080 if not name:
2081 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002082
2083 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002084 remote = self.GetRemote(name)
2085 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002086 ssh_proxy = True
2087
Shawn O. Pearce88443382010-10-08 10:02:09 +02002088 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002089 if alt_dir and 'objects' == os.path.basename(alt_dir):
2090 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002091 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2092 remote = self.GetRemote(name)
2093
David Pursehouse8a68ff92012-09-24 12:15:13 +09002094 all_refs = self.bare_ref.all
2095 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096 tmp = set()
2097
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302098 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002099 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002101 all_refs[r] = ref_id
2102 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002103 continue
2104
David Pursehouse8a68ff92012-09-24 12:15:13 +09002105 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 continue
2107
David Pursehouse8a68ff92012-09-24 12:15:13 +09002108 r = 'refs/_alt/%s' % ref_id
2109 all_refs[r] = ref_id
2110 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111 tmp.add(r)
2112
heping3d7bbc92017-04-12 19:51:47 +08002113 tmp_packed_lines = []
2114 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002115
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302116 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002117 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002118 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002119 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002120 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121
heping3d7bbc92017-04-12 19:51:47 +08002122 tmp_packed = ''.join(tmp_packed_lines)
2123 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002124 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002126 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002127
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002128 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002129
Conley Owensf97e8382015-01-21 11:12:46 -08002130 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002131 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002132 else:
2133 # If this repo has shallow objects, then we don't know which refs have
2134 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2135 # do this with projects that don't have shallow objects, since it is less
2136 # efficient.
2137 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2138 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002139
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002140 if quiet:
2141 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002142 if not self.worktree:
2143 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002144 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002145
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002146 # If using depth then we should not get all the tags since they may
2147 # be outside of the depth.
2148 if no_tags or depth:
2149 cmd.append('--no-tags')
2150 else:
2151 cmd.append('--tags')
2152
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002153 if force_sync:
2154 cmd.append('--force')
2155
David Pursehouse74cfd272015-10-14 10:50:15 +09002156 if prune:
2157 cmd.append('--prune')
2158
Martin Kellye4e94d22017-03-21 16:05:12 -07002159 if submodules:
2160 cmd.append('--recurse-submodules=on-demand')
2161
Conley Owens80b87fe2014-05-09 17:13:44 -07002162 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002163 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002164 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002165 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002166 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002167 spec.append('tag')
2168 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002169
David Pursehouse403b64e2015-04-27 10:41:33 +09002170 if not self.manifest.IsMirror:
2171 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002172 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002173 # Shallow checkout of a specific commit, fetch from that commit and not
2174 # the heads only as the commit might be deeper in the history.
2175 spec.append(branch)
2176 else:
2177 if is_sha1:
2178 branch = self.upstream
2179 if branch is not None and branch.strip():
2180 if not branch.startswith('refs/'):
2181 branch = R_HEADS + branch
2182 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002183 cmd.extend(spec)
2184
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002185 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002186 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002187 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002188 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002189 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002190 ok = True
2191 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002192 # If needed, run the 'git remote prune' the first time through the loop
2193 elif (not _i and
2194 "error:" in gitcmd.stderr and
2195 "git remote prune" in gitcmd.stderr):
2196 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002197 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002198 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002199 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002200 break
2201 continue
Brian Harring14a66742012-09-28 20:21:57 -07002202 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002203 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2204 # in sha1 mode, we just tried sync'ing from the upstream field; it
2205 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002206 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002207 elif ret < 0:
2208 # Git died with a signal, exit immediately
2209 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002210 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002211
2212 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002213 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002214 if old_packed != '':
2215 _lwrite(packed_refs, old_packed)
2216 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002217 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002218 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002219
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002220 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002221 # We just synced the upstream given branch; verify we
2222 # got what we wanted, else trigger a second run of all
2223 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002224 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002225 if current_branch_only and depth:
2226 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002227 return self._RemoteFetch(name=name,
2228 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002229 initial=False, quiet=quiet, alt_dir=alt_dir,
2230 depth=None)
2231 else:
2232 # Avoid infinite recursion: sync all branches with depth set to None
2233 return self._RemoteFetch(name=name, current_branch_only=False,
2234 initial=False, quiet=quiet, alt_dir=alt_dir,
2235 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002236
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002237 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002238
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002239 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002240 if initial and \
2241 (self.manifest.manifestProject.config.GetString('repo.depth') or
2242 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002243 return False
2244
2245 remote = self.GetRemote(self.remote.name)
2246 bundle_url = remote.url + '/clone.bundle'
2247 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002248 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2249 'persistent-http',
2250 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002251 return False
2252
2253 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2254 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2255
2256 exist_dst = os.path.exists(bundle_dst)
2257 exist_tmp = os.path.exists(bundle_tmp)
2258
2259 if not initial and not exist_dst and not exist_tmp:
2260 return False
2261
2262 if not exist_dst:
2263 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2264 if not exist_dst:
2265 return False
2266
2267 cmd = ['fetch']
2268 if quiet:
2269 cmd.append('--quiet')
2270 if not self.worktree:
2271 cmd.append('--update-head-ok')
2272 cmd.append(bundle_dst)
2273 for f in remote.fetch:
2274 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002275 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002276
2277 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002278 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002279 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002280 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002281 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002282 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002283
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002284 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002285 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002286 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002287
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002288 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002289 if quiet:
2290 cmd += ['--silent']
2291 if os.path.exists(tmpPath):
2292 size = os.stat(tmpPath).st_size
2293 if size >= 1024:
2294 cmd += ['--continue-at', '%d' % (size,)]
2295 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002296 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002297 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2298 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002299 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002300 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002301 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002302 if srcUrl.startswith('persistent-'):
2303 srcUrl = srcUrl[len('persistent-'):]
2304 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002305
Dave Borowitz137d0132015-01-02 11:12:54 -08002306 if IsTrace():
2307 Trace('%s', ' '.join(cmd))
2308 try:
2309 proc = subprocess.Popen(cmd)
2310 except OSError:
2311 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002312
Dave Borowitz137d0132015-01-02 11:12:54 -08002313 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002314
Dave Borowitz137d0132015-01-02 11:12:54 -08002315 if curlret == 22:
2316 # From curl man page:
2317 # 22: HTTP page not retrieved. The requested url was not found or
2318 # returned another error with the HTTP error code being 400 or above.
2319 # This return code only appears if -f, --fail is used.
2320 if not quiet:
2321 print("Server does not provide clone.bundle; ignoring.",
2322 file=sys.stderr)
2323 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002324
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002325 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002326 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002327 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002328 return True
2329 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002330 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002331 return False
2332 else:
2333 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002334
Kris Giesingc8d882a2014-12-23 13:02:32 -08002335 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002336 try:
2337 with open(path) as f:
2338 if f.read(16) == '# v2 git bundle\n':
2339 return True
2340 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002341 if not quiet:
2342 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002343 return False
2344 except OSError:
2345 return False
2346
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002347 def _Checkout(self, rev, quiet=False):
2348 cmd = ['checkout']
2349 if quiet:
2350 cmd.append('-q')
2351 cmd.append(rev)
2352 cmd.append('--')
2353 if GitCommand(self, cmd).Wait() != 0:
2354 if self._allrefs:
2355 raise GitError('%s checkout %s ' % (self.name, rev))
2356
Anthony King7bdac712014-07-16 12:56:40 +01002357 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002358 cmd = ['cherry-pick']
2359 cmd.append(rev)
2360 cmd.append('--')
2361 if GitCommand(self, cmd).Wait() != 0:
2362 if self._allrefs:
2363 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2364
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302365 def _LsRemote(self, refs):
2366 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302367 p = GitCommand(self, cmd, capture_stdout=True)
2368 if p.Wait() == 0:
2369 if hasattr(p.stdout, 'decode'):
2370 return p.stdout.decode('utf-8')
2371 else:
2372 return p.stdout
2373 return None
2374
Anthony King7bdac712014-07-16 12:56:40 +01002375 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002376 cmd = ['revert']
2377 cmd.append('--no-edit')
2378 cmd.append(rev)
2379 cmd.append('--')
2380 if GitCommand(self, cmd).Wait() != 0:
2381 if self._allrefs:
2382 raise GitError('%s revert %s ' % (self.name, rev))
2383
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002384 def _ResetHard(self, rev, quiet=True):
2385 cmd = ['reset', '--hard']
2386 if quiet:
2387 cmd.append('-q')
2388 cmd.append(rev)
2389 if GitCommand(self, cmd).Wait() != 0:
2390 raise GitError('%s reset --hard %s ' % (self.name, rev))
2391
Martin Kellye4e94d22017-03-21 16:05:12 -07002392 def _SyncSubmodules(self, quiet=True):
2393 cmd = ['submodule', 'update', '--init', '--recursive']
2394 if quiet:
2395 cmd.append('-q')
2396 if GitCommand(self, cmd).Wait() != 0:
2397 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2398
Anthony King7bdac712014-07-16 12:56:40 +01002399 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002400 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002401 if onto is not None:
2402 cmd.extend(['--onto', onto])
2403 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002404 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002405 raise GitError('%s rebase %s ' % (self.name, upstream))
2406
Pierre Tardy3d125942012-05-04 12:18:12 +02002407 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002408 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002409 if ffonly:
2410 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002411 if GitCommand(self, cmd).Wait() != 0:
2412 raise GitError('%s merge %s ' % (self.name, head))
2413
Kevin Degiabaa7f32014-11-12 11:27:45 -07002414 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002415 init_git_dir = not os.path.exists(self.gitdir)
2416 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002417 try:
2418 # Initialize the bare repository, which contains all of the objects.
2419 if init_obj_dir:
2420 os.makedirs(self.objdir)
2421 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002422
Kevin Degib1a07b82015-07-27 13:33:43 -06002423 # If we have a separate directory to hold refs, initialize it as well.
2424 if self.objdir != self.gitdir:
2425 if init_git_dir:
2426 os.makedirs(self.gitdir)
2427
2428 if init_obj_dir or init_git_dir:
2429 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2430 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002431 try:
2432 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2433 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002434 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002435 print("Retrying clone after deleting %s" %
2436 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002437 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002438 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2439 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002440 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002441 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002442 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2443 except:
2444 raise e
2445 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002446
Kevin Degi384b3c52014-10-16 16:02:58 -06002447 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002448 mp = self.manifest.manifestProject
2449 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002450
Kevin Degib1a07b82015-07-27 13:33:43 -06002451 if ref_dir or mirror_git:
2452 if not mirror_git:
2453 mirror_git = os.path.join(ref_dir, self.name + '.git')
2454 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2455 self.relpath + '.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
Shawn O. Pearce88443382010-10-08 10:02:09 +02002459
Kevin Degib1a07b82015-07-27 13:33:43 -06002460 elif os.path.exists(repo_git):
2461 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002462
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
Kevin Degib1a07b82015-07-27 13:33:43 -06002474 self._UpdateHooks()
2475
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
Jimmie Westera0444582012-10-24 13:44:42 +02002493 def _UpdateHooks(self):
2494 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002495 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002496
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002497 def _InitHooks(self):
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):
2517 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002518 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002519 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002520 _warn("%s: Not replacing locally modified %s hook",
2521 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002522 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002523 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002524 platform_utils.symlink(
2525 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002526 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002527 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002528 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002529 else:
2530 raise
2531
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002532 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002533 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002534 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002535 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002536 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002537 remote.review = self.remote.review
2538 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002540 if self.worktree:
2541 remote.ResetFetch(mirror=False)
2542 else:
2543 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002544 remote.Save()
2545
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002546 def _InitMRef(self):
2547 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002548 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002549
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002550 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002551 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002552
2553 def _InitAnyMRef(self, ref):
2554 cur = self.bare_ref.symref(ref)
2555
2556 if self.revisionId:
2557 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2558 msg = 'manifest set to %s' % self.revisionId
2559 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002560 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002561 else:
2562 remote = self.GetRemote(self.remote.name)
2563 dst = remote.ToLocal(self.revisionExpr)
2564 if cur != dst:
2565 msg = 'manifest set to %s' % self.revisionExpr
2566 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002567
Kevin Degi384b3c52014-10-16 16:02:58 -06002568 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002569 symlink_files = self.shareable_files[:]
2570 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002571 if share_refs:
2572 symlink_files += self.working_tree_files
2573 symlink_dirs += self.working_tree_dirs
2574 to_symlink = symlink_files + symlink_dirs
2575 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002576 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002577 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002578 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002579 # Fail if the links are pointing to the wrong place
2580 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002581 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002582 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002583 'work tree. If you\'re comfortable with the '
2584 'possibility of losing the work tree\'s git metadata,'
2585 ' use `repo sync --force-sync {0}` to '
2586 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002587
David James8d201162013-10-11 17:03:19 -07002588 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2589 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2590
2591 Args:
2592 gitdir: The bare git repository. Must already be initialized.
2593 dotgit: The repository you would like to initialize.
2594 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2595 Only one work tree can store refs under a given |gitdir|.
2596 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2597 This saves you the effort of initializing |dotgit| yourself.
2598 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002599 symlink_files = self.shareable_files[:]
2600 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002601 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002602 symlink_files += self.working_tree_files
2603 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002604 to_symlink = symlink_files + symlink_dirs
2605
2606 to_copy = []
2607 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002608 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002609
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002610 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002611 for name in set(to_copy).union(to_symlink):
2612 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002613 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002614 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002615
Kevin Degi384b3c52014-10-16 16:02:58 -06002616 if os.path.lexists(dst):
2617 continue
David James8d201162013-10-11 17:03:19 -07002618
2619 # If the source dir doesn't exist, create an empty dir.
2620 if name in symlink_dirs and not os.path.lexists(src):
2621 os.makedirs(src)
2622
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002623 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002624 platform_utils.symlink(
2625 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002626 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002627 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002628 shutil.copytree(src, dst)
2629 elif os.path.isfile(src):
2630 shutil.copy(src, dst)
2631
Conley Owens80b87fe2014-05-09 17:13:44 -07002632 # If the source file doesn't exist, ensure the destination
2633 # file doesn't either.
2634 if name in symlink_files and not os.path.lexists(src):
2635 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002636 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002637 except OSError:
2638 pass
2639
David James8d201162013-10-11 17:03:19 -07002640 except OSError as e:
2641 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002642 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002643 else:
2644 raise
2645
Martin Kellye4e94d22017-03-21 16:05:12 -07002646 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002647 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002648 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002649 try:
2650 if init_dotgit:
2651 os.makedirs(dotgit)
2652 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2653 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002654
Kevin Degiabaa7f32014-11-12 11:27:45 -07002655 try:
2656 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2657 except GitError as e:
2658 if force_sync:
2659 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002660 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002661 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002662 except:
2663 raise e
2664 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002665
Kevin Degib1a07b82015-07-27 13:33:43 -06002666 if init_dotgit:
2667 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002668
Kevin Degib1a07b82015-07-27 13:33:43 -06002669 cmd = ['read-tree', '--reset', '-u']
2670 cmd.append('-v')
2671 cmd.append(HEAD)
2672 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002673 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002674
Martin Kellye4e94d22017-03-21 16:05:12 -07002675 if submodules:
2676 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002677 self._CopyAndLinkFiles()
2678 except Exception:
2679 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002680 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002681 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002682
Renaud Paquay788e9622017-01-27 11:41:12 -08002683 def _get_symlink_error_message(self):
2684 if platform_utils.isWindows():
2685 return ('Unable to create symbolic link. Please re-run the command as '
2686 'Administrator, or see '
2687 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2688 'for other options.')
2689 return 'filesystem must support symlinks'
2690
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002691 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002692 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002693
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002694 def _revlist(self, *args, **kw):
2695 a = []
2696 a.extend(args)
2697 a.append('--')
2698 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002699
2700 @property
2701 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002702 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002703
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002704 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002705 """Get logs between two revisions of this project."""
2706 comp = '..'
2707 if rev1:
2708 revs = [rev1]
2709 if rev2:
2710 revs.extend([comp, rev2])
2711 cmd = ['log', ''.join(revs)]
2712 out = DiffColoring(self.config)
2713 if out.is_on and color:
2714 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002715 if pretty_format is not None:
2716 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002717 if oneline:
2718 cmd.append('--oneline')
2719
2720 try:
2721 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2722 if log.Wait() == 0:
2723 return log.stdout
2724 except GitError:
2725 # worktree may not exist if groups changed for example. In that case,
2726 # try in gitdir instead.
2727 if not os.path.exists(self.worktree):
2728 return self.bare_git.log(*cmd[1:])
2729 else:
2730 raise
2731 return None
2732
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002733 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2734 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002735 """Get the list of logs from this revision to given revisionId"""
2736 logs = {}
2737 selfId = self.GetRevisionId(self._allrefs)
2738 toId = toProject.GetRevisionId(toProject._allrefs)
2739
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002740 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2741 pretty_format=pretty_format)
2742 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2743 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002744 return logs
2745
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002747
David James8d201162013-10-11 17:03:19 -07002748 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002749 self._project = project
2750 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002751 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002752
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002753 def LsOthers(self):
2754 p = GitCommand(self._project,
2755 ['ls-files',
2756 '-z',
2757 '--others',
2758 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002759 bare=False,
David James8d201162013-10-11 17:03:19 -07002760 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002761 capture_stdout=True,
2762 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002763 if p.Wait() == 0:
2764 out = p.stdout
2765 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002766 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002767 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002768 return []
2769
2770 def DiffZ(self, name, *args):
2771 cmd = [name]
2772 cmd.append('-z')
Eli Ribble7b4f0192019-05-02 18:21:42 -07002773 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002774 cmd.extend(args)
2775 p = GitCommand(self._project,
2776 cmd,
David James8d201162013-10-11 17:03:19 -07002777 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002778 bare=False,
2779 capture_stdout=True,
2780 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002781 try:
2782 out = p.process.stdout.read()
2783 r = {}
2784 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002785 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002786 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002787 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002788 info = next(out)
2789 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002790 except StopIteration:
2791 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002792
2793 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002794
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002795 def __init__(self, path, omode, nmode, oid, nid, state):
2796 self.path = path
2797 self.src_path = None
2798 self.old_mode = omode
2799 self.new_mode = nmode
2800 self.old_id = oid
2801 self.new_id = nid
2802
2803 if len(state) == 1:
2804 self.status = state
2805 self.level = None
2806 else:
2807 self.status = state[:1]
2808 self.level = state[1:]
2809 while self.level.startswith('0'):
2810 self.level = self.level[1:]
2811
2812 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002813 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814 if info.status in ('R', 'C'):
2815 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002816 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002817 r[info.path] = info
2818 return r
2819 finally:
2820 p.Wait()
2821
2822 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002823 if self._bare:
2824 path = os.path.join(self._project.gitdir, HEAD)
2825 else:
2826 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002827 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002828 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002829 except IOError as e:
2830 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002831 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002832 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002833 finally:
2834 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302835 try:
2836 line = line.decode()
2837 except AttributeError:
2838 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002839 if line.startswith('ref: '):
2840 return line[5:-1]
2841 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002842
2843 def SetHead(self, ref, message=None):
2844 cmdv = []
2845 if message is not None:
2846 cmdv.extend(['-m', message])
2847 cmdv.append(HEAD)
2848 cmdv.append(ref)
2849 self.symbolic_ref(*cmdv)
2850
2851 def DetachHead(self, new, message=None):
2852 cmdv = ['--no-deref']
2853 if message is not None:
2854 cmdv.extend(['-m', message])
2855 cmdv.append(HEAD)
2856 cmdv.append(new)
2857 self.update_ref(*cmdv)
2858
2859 def UpdateRef(self, name, new, old=None,
2860 message=None,
2861 detach=False):
2862 cmdv = []
2863 if message is not None:
2864 cmdv.extend(['-m', message])
2865 if detach:
2866 cmdv.append('--no-deref')
2867 cmdv.append(name)
2868 cmdv.append(new)
2869 if old is not None:
2870 cmdv.append(old)
2871 self.update_ref(*cmdv)
2872
2873 def DeleteRef(self, name, old=None):
2874 if not old:
2875 old = self.rev_parse(name)
2876 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002877 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002878
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002879 def rev_list(self, *args, **kw):
2880 if 'format' in kw:
2881 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2882 else:
2883 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002884 cmdv.extend(args)
2885 p = GitCommand(self._project,
2886 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002887 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002888 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002889 capture_stdout=True,
2890 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002891 r = []
2892 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002893 if line[-1] == '\n':
2894 line = line[:-1]
2895 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002896 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002897 raise GitError('%s rev-list %s: %s' %
2898 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002899 return r
2900
2901 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002902 """Allow arbitrary git commands using pythonic syntax.
2903
2904 This allows you to do things like:
2905 git_obj.rev_parse('HEAD')
2906
2907 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2908 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002909 Any other positional arguments will be passed to the git command, and the
2910 following keyword arguments are supported:
2911 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002912
2913 Args:
2914 name: The name of the git command to call. Any '_' characters will
2915 be replaced with '-'.
2916
2917 Returns:
2918 A callable object that will try to call git with the named command.
2919 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002920 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002921
Dave Borowitz091f8932012-10-23 17:01:04 -07002922 def runner(*args, **kwargs):
2923 cmdv = []
2924 config = kwargs.pop('config', None)
2925 for k in kwargs:
2926 raise TypeError('%s() got an unexpected keyword argument %r'
2927 % (name, k))
2928 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002929 if not git_require((1, 7, 2)):
2930 raise ValueError('cannot set config on command line for %s()'
2931 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302932 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002933 cmdv.append('-c')
2934 cmdv.append('%s=%s' % (k, v))
2935 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002936 cmdv.extend(args)
2937 p = GitCommand(self._project,
2938 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002939 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002940 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002941 capture_stdout=True,
2942 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002944 raise GitError('%s %s: %s' %
2945 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002946 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302947 try:
Conley Owensedd01512013-09-26 12:59:58 -07002948 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302949 except AttributeError:
2950 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002951 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2952 return r[:-1]
2953 return r
2954 return runner
2955
2956
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002957class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002958
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002959 def __str__(self):
2960 return 'prior sync failed; rebase still in progress'
2961
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002962
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002963class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002964
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002965 def __str__(self):
2966 return 'contains uncommitted changes'
2967
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002968
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002969class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002970
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002971 def __init__(self, project, text):
2972 self.project = project
2973 self.text = text
2974
2975 def Print(self, syncbuf):
2976 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2977 syncbuf.out.nl()
2978
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002979
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002980class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002981
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002982 def __init__(self, project, why):
2983 self.project = project
2984 self.why = why
2985
2986 def Print(self, syncbuf):
2987 syncbuf.out.fail('error: %s/: %s',
2988 self.project.relpath,
2989 str(self.why))
2990 syncbuf.out.nl()
2991
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002992
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002993class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002994
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002995 def __init__(self, project, action):
2996 self.project = project
2997 self.action = action
2998
2999 def Run(self, syncbuf):
3000 out = syncbuf.out
3001 out.project('project %s/', self.project.relpath)
3002 out.nl()
3003 try:
3004 self.action()
3005 out.nl()
3006 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003007 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003008 out.nl()
3009 return False
3010
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003011
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003012class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003013
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003014 def __init__(self, config):
3015 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003016 self.project = self.printer('header', attr='bold')
3017 self.info = self.printer('info')
3018 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003019
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003020
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003021class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003022
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003023 def __init__(self, config, detach_head=False):
3024 self._messages = []
3025 self._failures = []
3026 self._later_queue1 = []
3027 self._later_queue2 = []
3028
3029 self.out = _SyncColoring(config)
3030 self.out.redirect(sys.stderr)
3031
3032 self.detach_head = detach_head
3033 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003034 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003035
3036 def info(self, project, fmt, *args):
3037 self._messages.append(_InfoMessage(project, fmt % args))
3038
3039 def fail(self, project, err=None):
3040 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003041 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003042
3043 def later1(self, project, what):
3044 self._later_queue1.append(_Later(project, what))
3045
3046 def later2(self, project, what):
3047 self._later_queue2.append(_Later(project, what))
3048
3049 def Finish(self):
3050 self._PrintMessages()
3051 self._RunLater()
3052 self._PrintMessages()
3053 return self.clean
3054
David Rileye0684ad2017-04-05 00:02:59 -07003055 def Recently(self):
3056 recent_clean = self.recent_clean
3057 self.recent_clean = True
3058 return recent_clean
3059
3060 def _MarkUnclean(self):
3061 self.clean = False
3062 self.recent_clean = False
3063
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003064 def _RunLater(self):
3065 for q in ['_later_queue1', '_later_queue2']:
3066 if not self._RunQueue(q):
3067 return
3068
3069 def _RunQueue(self, queue):
3070 for m in getattr(self, queue):
3071 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003072 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003073 return False
3074 setattr(self, queue, [])
3075 return True
3076
3077 def _PrintMessages(self):
3078 for m in self._messages:
3079 m.Print(self)
3080 for m in self._failures:
3081 m.Print(self)
3082
3083 self._messages = []
3084 self._failures = []
3085
3086
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003087class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003088
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003089 """A special project housed under .repo.
3090 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003091
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003092 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003093 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003094 manifest=manifest,
3095 name=name,
3096 gitdir=gitdir,
3097 objdir=gitdir,
3098 worktree=worktree,
3099 remote=RemoteSpec('origin'),
3100 relpath='.repo/%s' % name,
3101 revisionExpr='refs/heads/master',
3102 revisionId=None,
3103 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003104
3105 def PreSync(self):
3106 if self.Exists:
3107 cb = self.CurrentBranch
3108 if cb:
3109 base = self.GetBranch(cb).merge
3110 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003111 self.revisionExpr = base
3112 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003113
Martin Kelly224a31a2017-07-10 14:46:25 -07003114 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003115 """ Prepare MetaProject for manifest branch switch
3116 """
3117
3118 # detach and delete manifest branch, allowing a new
3119 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003120 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003121 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003122 syncbuf.Finish()
3123
3124 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003125 ['update-ref', '-d', 'refs/heads/default'],
3126 capture_stdout=True,
3127 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003128
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003129 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003130 def LastFetch(self):
3131 try:
3132 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3133 return os.path.getmtime(fh)
3134 except OSError:
3135 return 0
3136
3137 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003138 def HasChanges(self):
3139 """Has the remote received new commits not yet checked out?
3140 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003141 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003142 return False
3143
David Pursehouse8a68ff92012-09-24 12:15:13 +09003144 all_refs = self.bare_ref.all
3145 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003146 head = self.work_git.GetHead()
3147 if head.startswith(R_HEADS):
3148 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003149 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003150 except KeyError:
3151 head = None
3152
3153 if revid == head:
3154 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003155 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003156 return True
3157 return False