blob: ae2bf6f3166d42be2dc53eea6c9437d478e2a771 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
2#
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080018import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070020import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070022import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import re
24import shutil
25import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070026import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020028import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080029import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070030import time
Dave Borowitz137d0132015-01-02 11:12:54 -080031import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070032
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070034from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070035from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
36 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070037from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080038from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080039from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070040import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070041from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Shawn O. Pearced237b692009-04-17 18:49:50 -070043from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044
David Pursehouse59bbb582013-05-17 10:49:33 +090045from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040046if is_python3():
47 import urllib.parse
48else:
49 import imp
50 import urlparse
51 urllib = imp.new_module('urllib')
52 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070066 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080068 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700106 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700107 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700108 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000179 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200180 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700181 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200182 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200183 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800184 validate_certs=True,
185 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800186 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700187 people,
Brian Harring435370c2012-07-28 15:37:04 -0700188 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000189 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200190 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700191 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200192 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200193 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800194 validate_certs=validate_certs,
195 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700196
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700197 def GetPublishedRefs(self):
198 refs = {}
199 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700200 self.branch.remote.SshReviewUrl(self.project.UserEmail),
201 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700202 for line in output.split('\n'):
203 try:
204 (sha, ref) = line.split()
205 refs[sha] = ref
206 except ValueError:
207 pass
208
209 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700211
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700213
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214 def __init__(self, config):
215 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100216 self.project = self.printer('header', attr='bold')
217 self.branch = self.printer('header', attr='bold')
218 self.nobranch = self.printer('nobranch', fg='red')
219 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700220
Anthony King7bdac712014-07-16 12:56:40 +0100221 self.added = self.printer('added', fg='green')
222 self.changed = self.printer('changed', fg='red')
223 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700224
225
226class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700227
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700228 def __init__(self, config):
229 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100230 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700231
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700232
Anthony King7bdac712014-07-16 12:56:40 +0100233class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700234
James W. Mills24c13082012-04-12 15:04:13 -0500235 def __init__(self, name, value, keep):
236 self.name = name
237 self.value = value
238 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700239
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700240
Anthony King7bdac712014-07-16 12:56:40 +0100241class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700242
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800243 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244 self.src = src
245 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800246 self.abs_src = abssrc
247 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248
249 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800250 src = self.abs_src
251 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252 # copy file if it does not exist or is out of date
253 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
254 try:
255 # remove existing file first, since it might be read-only
256 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800257 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400258 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200259 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700260 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200261 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700262 shutil.copy(src, dest)
263 # make the file read-only
264 mode = os.stat(dest)[stat.ST_MODE]
265 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
266 os.chmod(dest, mode)
267 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700268 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700270
Anthony King7bdac712014-07-16 12:56:40 +0100271class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700272
Wink Saville4c426ef2015-06-03 08:05:17 -0700273 def __init__(self, git_worktree, src, dest, relsrc, absdest):
274 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500275 self.src = src
276 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700277 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500278 self.abs_dest = absdest
279
Wink Saville4c426ef2015-06-03 08:05:17 -0700280 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700282 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 try:
284 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800285 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800286 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500287 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700288 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700289 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700291 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500292 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700293 _error('Cannot link file %s to %s', relSrc, absDest)
294
295 def _Link(self):
296 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
297 on the src linking all of the files in the source in to the destination
298 directory.
299 """
300 # We use the absSrc to handle the situation where the current directory
301 # is not the root of the repo
302 absSrc = os.path.join(self.git_worktree, self.src)
303 if os.path.exists(absSrc):
304 # Entity exists so just a simple one to one link operation
305 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
306 else:
307 # Entity doesn't exist assume there is a wild card
308 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700309 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700310 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700311 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700312 else:
313 absSrcFiles = glob.glob(absSrc)
314 for absSrcFile in absSrcFiles:
315 # Create a releative path from source dir to destination dir
316 absSrcDir = os.path.dirname(absSrcFile)
317 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
318
319 # Get the source file name
320 srcFile = os.path.basename(absSrcFile)
321
322 # Now form the final full paths to srcFile. They will be
323 # absolute for the desintaiton and relative for the srouce.
324 absDest = os.path.join(absDestDir, srcFile)
325 relSrc = os.path.join(relSrcDir, srcFile)
326 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500327
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700328
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700329class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700330
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700331 def __init__(self,
332 name,
Anthony King7bdac712014-07-16 12:56:40 +0100333 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700334 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100335 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700336 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700337 orig_name=None,
338 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700339 self.name = name
340 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700341 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700342 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100343 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700344 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700345 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700346
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700347
Doug Anderson37282b42011-03-04 11:54:18 -0800348class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Doug Anderson37282b42011-03-04 11:54:18 -0800350 """A RepoHook contains information about a script to run as a hook.
351
352 Hooks are used to run a python script before running an upload (for instance,
353 to run presubmit checks). Eventually, we may have hooks for other actions.
354
355 This shouldn't be confused with files in the 'repo/hooks' directory. Those
356 files are copied into each '.git/hooks' folder for each project. Repo-level
357 hooks are associated instead with repo actions.
358
359 Hooks are always python. When a hook is run, we will load the hook into the
360 interpreter and execute its main() function.
361 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700362
Doug Anderson37282b42011-03-04 11:54:18 -0800363 def __init__(self,
364 hook_type,
365 hooks_project,
366 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400367 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800368 abort_if_user_denies=False):
369 """RepoHook constructor.
370
371 Params:
372 hook_type: A string representing the type of hook. This is also used
373 to figure out the name of the file containing the hook. For
374 example: 'pre-upload'.
375 hooks_project: The project containing the repo hooks. If you have a
376 manifest, this is manifest.repo_hooks_project. OK if this is None,
377 which will make the hook a no-op.
378 topdir: Repo's top directory (the one containing the .repo directory).
379 Scripts will run with CWD as this directory. If you have a manifest,
380 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400381 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800382 abort_if_user_denies: If True, we'll throw a HookError() if the user
383 doesn't allow us to run the hook.
384 """
385 self._hook_type = hook_type
386 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400387 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800388 self._topdir = topdir
389 self._abort_if_user_denies = abort_if_user_denies
390
391 # Store the full path to the script for convenience.
392 if self._hooks_project:
393 self._script_fullpath = os.path.join(self._hooks_project.worktree,
394 self._hook_type + '.py')
395 else:
396 self._script_fullpath = None
397
398 def _GetHash(self):
399 """Return a hash of the contents of the hooks directory.
400
401 We'll just use git to do this. This hash has the property that if anything
402 changes in the directory we will return a different has.
403
404 SECURITY CONSIDERATION:
405 This hash only represents the contents of files in the hook directory, not
406 any other files imported or called by hooks. Changes to imported files
407 can change the script behavior without affecting the hash.
408
409 Returns:
410 A string representing the hash. This will always be ASCII so that it can
411 be printed to the user easily.
412 """
413 assert self._hooks_project, "Must have hooks to calculate their hash."
414
415 # We will use the work_git object rather than just calling GetRevisionId().
416 # That gives us a hash of the latest checked in version of the files that
417 # the user will actually be executing. Specifically, GetRevisionId()
418 # doesn't appear to change even if a user checks out a different version
419 # of the hooks repo (via git checkout) nor if a user commits their own revs.
420 #
421 # NOTE: Local (non-committed) changes will not be factored into this hash.
422 # I think this is OK, since we're really only worried about warning the user
423 # about upstream changes.
424 return self._hooks_project.work_git.rev_parse('HEAD')
425
426 def _GetMustVerb(self):
427 """Return 'must' if the hook is required; 'should' if not."""
428 if self._abort_if_user_denies:
429 return 'must'
430 else:
431 return 'should'
432
433 def _CheckForHookApproval(self):
434 """Check to see whether this hook has been approved.
435
Mike Frysinger40252c22016-08-15 21:23:44 -0400436 We'll accept approval of manifest URLs if they're using secure transports.
437 This way the user can say they trust the manifest hoster. For insecure
438 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800439
440 Note that we ask permission for each individual hook even though we use
441 the hash of all hooks when detecting changes. We'd like the user to be
442 able to approve / deny each hook individually. We only use the hash of all
443 hooks because there is no other easy way to detect changes to local imports.
444
445 Returns:
446 True if this hook is approved to run; False otherwise.
447
448 Raises:
449 HookError: Raised if the user doesn't approve and abort_if_user_denies
450 was passed to the consturctor.
451 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400452 if self._ManifestUrlHasSecureScheme():
453 return self._CheckForHookApprovalManifest()
454 else:
455 return self._CheckForHookApprovalHash()
456
457 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
458 changed_prompt):
459 """Check for approval for a particular attribute and hook.
460
461 Args:
462 subkey: The git config key under [repo.hooks.<hook_type>] to store the
463 last approved string.
464 new_val: The new value to compare against the last approved one.
465 main_prompt: Message to display to the user to ask for approval.
466 changed_prompt: Message explaining why we're re-asking for approval.
467
468 Returns:
469 True if this hook is approved to run; False otherwise.
Doug Anderson37282b42011-03-04 11:54:18 -0800470
Mike Frysinger40252c22016-08-15 21:23:44 -0400471 Raises:
472 HookError: Raised if the user doesn't approve and abort_if_user_denies
473 was passed to the consturctor.
474 """
475 hooks_config = self._hooks_project.config
476 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800477
Mike Frysinger40252c22016-08-15 21:23:44 -0400478 # Get the last value that the user approved for this hook; may be None.
479 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800480
Mike Frysinger40252c22016-08-15 21:23:44 -0400481 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800482 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400483 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800484 # Approval matched. We're done.
485 return True
486 else:
487 # Give the user a reason why we're prompting, since they last told
488 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400489 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800490 else:
491 prompt = ''
492
493 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
494 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400495 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530496 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900497 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800498
499 # User is doing a one-time approval.
500 if response in ('y', 'yes'):
501 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400502 elif response == 'always':
503 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800504 return True
505
506 # For anything else, we'll assume no approval.
507 if self._abort_if_user_denies:
508 raise HookError('You must allow the %s hook or use --no-verify.' %
509 self._hook_type)
510
511 return False
512
Mike Frysinger40252c22016-08-15 21:23:44 -0400513 def _ManifestUrlHasSecureScheme(self):
514 """Check if the URI for the manifest is a secure transport."""
515 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
516 parse_results = urllib.parse.urlparse(self._manifest_url)
517 return parse_results.scheme in secure_schemes
518
519 def _CheckForHookApprovalManifest(self):
520 """Check whether the user has approved this manifest host.
521
522 Returns:
523 True if this hook is approved to run; False otherwise.
524 """
525 return self._CheckForHookApprovalHelper(
526 'approvedmanifest',
527 self._manifest_url,
528 'Run hook scripts from %s' % (self._manifest_url,),
529 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
530
531 def _CheckForHookApprovalHash(self):
532 """Check whether the user has approved the hooks repo.
533
534 Returns:
535 True if this hook is approved to run; False otherwise.
536 """
537 prompt = ('Repo %s run the script:\n'
538 ' %s\n'
539 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700540 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400541 return self._CheckForHookApprovalHelper(
542 'approvedhash',
543 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700544 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400545 'Scripts have changed since %s was allowed.' % (self._hook_type,))
546
Doug Anderson37282b42011-03-04 11:54:18 -0800547 def _ExecuteHook(self, **kwargs):
548 """Actually execute the given hook.
549
550 This will run the hook's 'main' function in our python interpreter.
551
552 Args:
553 kwargs: Keyword arguments to pass to the hook. These are often specific
554 to the hook type. For instance, pre-upload hooks will contain
555 a project_list.
556 """
557 # Keep sys.path and CWD stashed away so that we can always restore them
558 # upon function exit.
559 orig_path = os.getcwd()
560 orig_syspath = sys.path
561
562 try:
563 # Always run hooks with CWD as topdir.
564 os.chdir(self._topdir)
565
566 # Put the hook dir as the first item of sys.path so hooks can do
567 # relative imports. We want to replace the repo dir as [0] so
568 # hooks can't import repo files.
569 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
570
571 # Exec, storing global context in the context dict. We catch exceptions
572 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500573 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800574 try:
Anthony King70f68902014-05-05 21:15:34 +0100575 exec(compile(open(self._script_fullpath).read(),
576 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800577 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700578 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
579 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800580
581 # Running the script should have defined a main() function.
582 if 'main' not in context:
583 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
584
Doug Anderson37282b42011-03-04 11:54:18 -0800585 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
586 # We don't actually want hooks to define their main with this argument--
587 # it's there to remind them that their hook should always take **kwargs.
588 # For instance, a pre-upload hook should be defined like:
589 # def main(project_list, **kwargs):
590 #
591 # This allows us to later expand the API without breaking old hooks.
592 kwargs = kwargs.copy()
593 kwargs['hook_should_take_kwargs'] = True
594
595 # Call the main function in the hook. If the hook should cause the
596 # build to fail, it will raise an Exception. We'll catch that convert
597 # to a HookError w/ just the failing traceback.
598 try:
599 context['main'](**kwargs)
600 except Exception:
601 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700602 'above.' % (traceback.format_exc(),
603 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800604 finally:
605 # Restore sys.path and CWD.
606 sys.path = orig_syspath
607 os.chdir(orig_path)
608
609 def Run(self, user_allows_all_hooks, **kwargs):
610 """Run the hook.
611
612 If the hook doesn't exist (because there is no hooks project or because
613 this particular hook is not enabled), this is a no-op.
614
615 Args:
616 user_allows_all_hooks: If True, we will never prompt about running the
617 hook--we'll just assume it's OK to run it.
618 kwargs: Keyword arguments to pass to the hook. These are often specific
619 to the hook type. For instance, pre-upload hooks will contain
620 a project_list.
621
622 Raises:
623 HookError: If there was a problem finding the hook or the user declined
624 to run a required hook (from _CheckForHookApproval).
625 """
626 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700627 if ((not self._hooks_project) or (self._hook_type not in
628 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800629 return
630
631 # Bail with a nice error if we can't find the hook.
632 if not os.path.isfile(self._script_fullpath):
633 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
634
635 # Make sure the user is OK with running the hook.
636 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
637 return
638
639 # Run the hook with the same version of python we're using.
640 self._ExecuteHook(**kwargs)
641
642
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700643class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600644 # These objects can be shared between several working trees.
645 shareable_files = ['description', 'info']
646 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
647 # These objects can only be used by a single working tree.
648 working_tree_files = ['config', 'packed-refs', 'shallow']
649 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700650
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700651 def __init__(self,
652 manifest,
653 name,
654 remote,
655 gitdir,
David James8d201162013-10-11 17:03:19 -0700656 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700657 worktree,
658 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700659 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800660 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100661 rebase=True,
662 groups=None,
663 sync_c=False,
664 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900665 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100666 clone_depth=None,
667 upstream=None,
668 parent=None,
669 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900670 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700671 optimized_fetch=False,
672 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800673 """Init a Project object.
674
675 Args:
676 manifest: The XmlManifest object.
677 name: The `name` attribute of manifest.xml's project element.
678 remote: RemoteSpec object specifying its remote's properties.
679 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700680 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800681 worktree: Absolute path of git working tree.
682 relpath: Relative path of git working tree to repo's top directory.
683 revisionExpr: The `revision` attribute of manifest.xml's project element.
684 revisionId: git commit id for checking out.
685 rebase: The `rebase` attribute of manifest.xml's project element.
686 groups: The `groups` attribute of manifest.xml's project element.
687 sync_c: The `sync-c` attribute of manifest.xml's project element.
688 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900689 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800690 upstream: The `upstream` attribute of manifest.xml's project element.
691 parent: The parent Project object.
692 is_derived: False if the project was explicitly defined in the manifest;
693 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400694 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900695 optimized_fetch: If True, when a project is set to a sha1 revision, only
696 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700697 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800698 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700699 self.manifest = manifest
700 self.name = name
701 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800702 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700703 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800704 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700705 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800706 else:
707 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700708 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700709 self.revisionExpr = revisionExpr
710
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700711 if revisionId is None \
712 and revisionExpr \
713 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700714 self.revisionId = revisionExpr
715 else:
716 self.revisionId = revisionId
717
Mike Pontillod3153822012-02-28 11:53:24 -0800718 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700719 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700720 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800721 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900722 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900723 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700724 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800725 self.parent = parent
726 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900727 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800728 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800729
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700731 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500732 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500733 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700734 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
735 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800737 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700738 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800739 else:
740 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700741 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700742 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700743 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400744 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700745 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700746
Doug Anderson37282b42011-03-04 11:54:18 -0800747 # This will be filled in if a project is later identified to be the
748 # project containing repo hooks.
749 self.enabled_repo_hooks = []
750
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800752 def Derived(self):
753 return self.is_derived
754
755 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700757 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758
759 @property
760 def CurrentBranch(self):
761 """Obtain the name of the currently checked out branch.
762 The branch name omits the 'refs/heads/' prefix.
763 None is returned if the project is on a detached HEAD.
764 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700765 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700766 if b.startswith(R_HEADS):
767 return b[len(R_HEADS):]
768 return None
769
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700770 def IsRebaseInProgress(self):
771 w = self.worktree
772 g = os.path.join(w, '.git')
773 return os.path.exists(os.path.join(g, 'rebase-apply')) \
774 or os.path.exists(os.path.join(g, 'rebase-merge')) \
775 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200776
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700777 def IsDirty(self, consider_untracked=True):
778 """Is the working directory modified in some way?
779 """
780 self.work_git.update_index('-q',
781 '--unmerged',
782 '--ignore-missing',
783 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900784 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700785 return True
786 if self.work_git.DiffZ('diff-files'):
787 return True
788 if consider_untracked and self.work_git.LsOthers():
789 return True
790 return False
791
792 _userident_name = None
793 _userident_email = None
794
795 @property
796 def UserName(self):
797 """Obtain the user's personal name.
798 """
799 if self._userident_name is None:
800 self._LoadUserIdentity()
801 return self._userident_name
802
803 @property
804 def UserEmail(self):
805 """Obtain the user's email address. This is very likely
806 to be their Gerrit login.
807 """
808 if self._userident_email is None:
809 self._LoadUserIdentity()
810 return self._userident_email
811
812 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900813 u = self.bare_git.var('GIT_COMMITTER_IDENT')
814 m = re.compile("^(.*) <([^>]*)> ").match(u)
815 if m:
816 self._userident_name = m.group(1)
817 self._userident_email = m.group(2)
818 else:
819 self._userident_name = ''
820 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821
822 def GetRemote(self, name):
823 """Get the configuration for a single remote.
824 """
825 return self.config.GetRemote(name)
826
827 def GetBranch(self, name):
828 """Get the configuration for a single branch.
829 """
830 return self.config.GetBranch(name)
831
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700832 def GetBranches(self):
833 """Get all existing local branches.
834 """
835 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900836 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700838
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530839 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700840 if name.startswith(R_HEADS):
841 name = name[len(R_HEADS):]
842 b = self.GetBranch(name)
843 b.current = name == current
844 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900845 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700846 heads[name] = b
847
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530848 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700849 if name.startswith(R_PUB):
850 name = name[len(R_PUB):]
851 b = heads.get(name)
852 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900853 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700854
855 return heads
856
Colin Cross5acde752012-03-28 20:15:45 -0700857 def MatchesGroups(self, manifest_groups):
858 """Returns true if the manifest groups specified at init should cause
859 this project to be synced.
860 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700861 All projects are implicitly labelled with "all".
Colin Cross5acde752012-03-28 20:15:45 -0700862
Conley Owens971de8e2012-04-16 10:36:08 -0700863 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700864 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700865 manifest_groups: "-group1,group2"
866 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500867
868 The special manifest group "default" will match any project that
869 does not have the special project group "notdefault"
Conley Owens971de8e2012-04-16 10:36:08 -0700870 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500871 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700872 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700873 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500874 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700875
Conley Owens971de8e2012-04-16 10:36:08 -0700876 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700877 for group in expanded_manifest_groups:
878 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700879 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700880 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700881 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700882
Conley Owens971de8e2012-04-16 10:36:08 -0700883 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700885# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700886 def UncommitedFiles(self, get_all=True):
887 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700889 Args:
890 get_all: a boolean, if True - get information about all different
891 uncommitted files. If False - return as soon as any kind of
892 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500893 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700894 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500895 self.work_git.update_index('-q',
896 '--unmerged',
897 '--ignore-missing',
898 '--refresh')
899 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700900 details.append("rebase in progress")
901 if not get_all:
902 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500903
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700904 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
905 if changes:
906 details.extend(changes)
907 if not get_all:
908 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500909
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700910 changes = self.work_git.DiffZ('diff-files').keys()
911 if changes:
912 details.extend(changes)
913 if not get_all:
914 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500915
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700916 changes = self.work_git.LsOthers()
917 if changes:
918 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500919
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700920 return details
921
922 def HasChanges(self):
923 """Returns true if there are uncommitted changes.
924 """
925 if self.UncommitedFiles(get_all=False):
926 return True
927 else:
928 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500929
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600930 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700931 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200932
933 Args:
934 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600935 quiet: If True then only print the project name. Do not print
936 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700937 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700938 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700939 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200940 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700941 print(file=output_redir)
942 print('project %s/' % self.relpath, file=output_redir)
943 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944 return
945
946 self.work_git.update_index('-q',
947 '--unmerged',
948 '--ignore-missing',
949 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700950 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700951 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
952 df = self.work_git.DiffZ('diff-files')
953 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100954 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700955 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700956
957 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700958 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200959 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700960 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600962 if quiet:
963 out.nl()
964 return 'DIRTY'
965
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700966 branch = self.CurrentBranch
967 if branch is None:
968 out.nobranch('(*** NO BRANCH ***)')
969 else:
970 out.branch('branch %s', branch)
971 out.nl()
972
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700973 if rb:
974 out.important('prior sync failed; rebase still in progress')
975 out.nl()
976
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700977 paths = list()
978 paths.extend(di.keys())
979 paths.extend(df.keys())
980 paths.extend(do)
981
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530982 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900983 try:
984 i = di[p]
985 except KeyError:
986 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700987
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900988 try:
989 f = df[p]
990 except KeyError:
991 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200992
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900993 if i:
994 i_status = i.status.upper()
995 else:
996 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900998 if f:
999 f_status = f.status.lower()
1000 else:
1001 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002
1003 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001004 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001005 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006 else:
1007 line = ' %s%s\t%s' % (i_status, f_status, p)
1008
1009 if i and not f:
1010 out.added('%s', line)
1011 elif (i and f) or (not i and f):
1012 out.changed('%s', line)
1013 elif not i and not f:
1014 out.untracked('%s', line)
1015 else:
1016 out.write('%s', line)
1017 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001018
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001019 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020
pelyad67872d2012-03-28 14:49:58 +03001021 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022 """Prints the status of the repository to stdout.
1023 """
1024 out = DiffColoring(self.config)
1025 cmd = ['diff']
1026 if out.is_on:
1027 cmd.append('--color')
1028 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001029 if absolute_paths:
1030 cmd.append('--src-prefix=a/%s/' % self.relpath)
1031 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032 cmd.append('--')
1033 p = GitCommand(self,
1034 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001035 capture_stdout=True,
1036 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 has_diff = False
1038 for line in p.process.stdout:
1039 if not has_diff:
1040 out.nl()
1041 out.project('project %s/' % self.relpath)
1042 out.nl()
1043 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001044 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045 p.Wait()
1046
1047
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001048# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049
David Pursehouse8a68ff92012-09-24 12:15:13 +09001050 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001051 """Was the branch published (uploaded) for code review?
1052 If so, returns the SHA-1 hash of the last published
1053 state for the branch.
1054 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001056 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001057 try:
1058 return self.bare_git.rev_parse(key)
1059 except GitError:
1060 return None
1061 else:
1062 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001063 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001064 except KeyError:
1065 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066
David Pursehouse8a68ff92012-09-24 12:15:13 +09001067 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 """Prunes any stale published refs.
1069 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001070 if all_refs is None:
1071 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 heads = set()
1073 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301074 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075 if name.startswith(R_HEADS):
1076 heads.add(name)
1077 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001078 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301080 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 n = name[len(R_PUB):]
1082 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001083 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001085 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001086 """List any branches which can be uploaded for review.
1087 """
1088 heads = {}
1089 pubed = {}
1090
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301091 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001093 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001095 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001096
1097 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301098 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001099 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001100 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001101 if selected_branch and branch != selected_branch:
1102 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001104 rb = self.GetUploadableBranch(branch)
1105 if rb:
1106 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001107 return ready
1108
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001109 def GetUploadableBranch(self, branch_name):
1110 """Get a single uploadable branch, or None.
1111 """
1112 branch = self.GetBranch(branch_name)
1113 base = branch.LocalMerge
1114 if branch.LocalMerge:
1115 rb = ReviewableBranch(self, branch, base)
1116 if rb.commits:
1117 return rb
1118 return None
1119
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001120 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001121 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001122 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001123 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001124 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001125 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001126 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001127 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001128 validate_certs=True,
1129 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001130 """Uploads the named branch for code review.
1131 """
1132 if branch is None:
1133 branch = self.CurrentBranch
1134 if branch is None:
1135 raise GitError('not currently on a branch')
1136
1137 branch = self.GetBranch(branch)
1138 if not branch.LocalMerge:
1139 raise GitError('branch %s does not track a remote' % branch.name)
1140 if not branch.remote.review:
1141 raise GitError('remote %s has no review url' % branch.remote.name)
1142
Bryan Jacobsf609f912013-05-06 13:36:24 -04001143 if dest_branch is None:
1144 dest_branch = self.dest_branch
1145 if dest_branch is None:
1146 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001147 if not dest_branch.startswith(R_HEADS):
1148 dest_branch = R_HEADS + dest_branch
1149
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001150 if not branch.remote.projectname:
1151 branch.remote.projectname = self.name
1152 branch.remote.Save()
1153
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001154 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001155 if url is None:
1156 raise UploadError('review not configured')
1157 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001158
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001159 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001160 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001161
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001162 for push_option in (push_options or []):
1163 cmd.append('-o')
1164 cmd.append(push_option)
1165
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 cmd.append(url)
1167
1168 if dest_branch.startswith(R_HEADS):
1169 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001170
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001171 upload_type = 'for'
1172 if draft:
1173 upload_type = 'drafts'
1174
1175 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1176 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001177 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001178 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001179 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001180
David Pursehousef25a3702018-11-14 19:01:22 -08001181 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001182 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001183 if notify:
1184 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001185 if private:
1186 opts += ['private']
1187 if wip:
1188 opts += ['wip']
1189 if opts:
1190 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001191 cmd.append(ref_spec)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001192
Anthony King7bdac712014-07-16 12:56:40 +01001193 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001194 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195
1196 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1197 self.bare_git.UpdateRef(R_PUB + branch.name,
1198 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001199 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200
1201
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001202# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203
Julien Campergue335f5ef2013-10-16 11:02:35 +02001204 def _ExtractArchive(self, tarpath, path=None):
1205 """Extract the given tar on its current location
1206
1207 Args:
1208 - tarpath: The path to the actual tar file
1209
1210 """
1211 try:
1212 with tarfile.open(tarpath, 'r') as tar:
1213 tar.extractall(path=path)
1214 return True
1215 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001216 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001217 return False
1218
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001219 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001220 quiet=False,
1221 is_new=None,
1222 current_branch_only=False,
1223 force_sync=False,
1224 clone_bundle=True,
1225 no_tags=False,
1226 archive=False,
1227 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001228 prune=False,
1229 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 """Perform only the network IO portion of the sync process.
1231 Local working directory/branch state is not affected.
1232 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001233 if archive and not isinstance(self, MetaProject):
1234 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001235 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001236 return False
1237
1238 name = self.relpath.replace('\\', '/')
1239 name = name.replace('/', '_')
1240 tarpath = '%s.tar' % name
1241 topdir = self.manifest.topdir
1242
1243 try:
1244 self._FetchArchive(tarpath, cwd=topdir)
1245 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001246 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001247 return False
1248
1249 # From now on, we only need absolute tarpath
1250 tarpath = os.path.join(topdir, tarpath)
1251
1252 if not self._ExtractArchive(tarpath, path=topdir):
1253 return False
1254 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001255 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001256 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001257 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001258 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001259 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001260 if is_new is None:
1261 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001262 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001263 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001264 else:
1265 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001267
1268 if is_new:
1269 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1270 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001271 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001272 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001273 # This works for both absolute and relative alternate directories.
1274 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001275 finally:
1276 fd.close()
1277 except IOError:
1278 alt_dir = None
1279 else:
1280 alt_dir = None
1281
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001282 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001283 and alt_dir is None \
1284 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001285 is_new = False
1286
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001287 if not current_branch_only:
1288 if self.sync_c:
1289 current_branch_only = True
1290 elif not self.manifest._loaded:
1291 # Manifest cannot check defaults until it syncs.
1292 current_branch_only = False
1293 elif self.manifest.default.sync_c:
1294 current_branch_only = True
1295
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001296 if not no_tags:
1297 if not self.sync_tags:
1298 no_tags = True
1299
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001300 if self.clone_depth:
1301 depth = self.clone_depth
1302 else:
1303 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1304
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001305 need_to_fetch = not (optimized_fetch and
1306 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001307 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001308 if (need_to_fetch and
1309 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1310 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001311 no_tags=no_tags, prune=prune, depth=depth,
Mike Frysingere57f1142019-03-18 21:27:54 -04001312 submodules=submodules, force_sync=force_sync)):
Anthony King7bdac712014-07-16 12:56:40 +01001313 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001314
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001315 mp = self.manifest.manifestProject
1316 dissociate = mp.config.GetBoolean('repo.dissociate')
1317 if dissociate:
1318 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1319 if os.path.exists(alternates_file):
1320 cmd = ['repack', '-a', '-d']
1321 if GitCommand(self, cmd, bare=True).Wait() != 0:
1322 return False
1323 platform_utils.remove(alternates_file)
1324
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001325 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001326 self._InitMRef()
1327 else:
1328 self._InitMirrorHead()
1329 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001330 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001331 except OSError:
1332 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001333 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001334
1335 def PostRepoUpgrade(self):
1336 self._InitHooks()
1337
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001338 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001339 if self.manifest.isGitcClient:
1340 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001341 for copyfile in self.copyfiles:
1342 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001343 for linkfile in self.linkfiles:
1344 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001345
Julien Camperguedd654222014-01-09 16:21:37 +01001346 def GetCommitRevisionId(self):
1347 """Get revisionId of a commit.
1348
1349 Use this method instead of GetRevisionId to get the id of the commit rather
1350 than the id of the current git object (for example, a tag)
1351
1352 """
1353 if not self.revisionExpr.startswith(R_TAGS):
1354 return self.GetRevisionId(self._allrefs)
1355
1356 try:
1357 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1358 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001359 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1360 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001361
David Pursehouse8a68ff92012-09-24 12:15:13 +09001362 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001363 if self.revisionId:
1364 return self.revisionId
1365
1366 rem = self.GetRemote(self.remote.name)
1367 rev = rem.ToLocal(self.revisionExpr)
1368
David Pursehouse8a68ff92012-09-24 12:15:13 +09001369 if all_refs is not None and rev in all_refs:
1370 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001371
1372 try:
1373 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1374 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001375 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1376 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001377
Martin Kellye4e94d22017-03-21 16:05:12 -07001378 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001379 """Perform only the local IO portion of the sync process.
1380 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001381 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001382 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001383 all_refs = self.bare_ref.all
1384 self.CleanPublishedCache(all_refs)
1385 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001386
David Pursehouse1d947b32012-10-25 12:23:11 +09001387 def _doff():
1388 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001389 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001390
Martin Kellye4e94d22017-03-21 16:05:12 -07001391 def _dosubmodules():
1392 self._SyncSubmodules(quiet=True)
1393
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001394 head = self.work_git.GetHead()
1395 if head.startswith(R_HEADS):
1396 branch = head[len(R_HEADS):]
1397 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001398 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001399 except KeyError:
1400 head = None
1401 else:
1402 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001403
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001404 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405 # Currently on a detached HEAD. The user is assumed to
1406 # not have any local modifications worth worrying about.
1407 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001408 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001409 syncbuf.fail(self, _PriorSyncFailedError())
1410 return
1411
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001412 if head == revid:
1413 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001414 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001415 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001416 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001417 # The copy/linkfile config may have changed.
1418 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001419 return
1420 else:
1421 lost = self._revlist(not_rev(revid), HEAD)
1422 if lost:
1423 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001424
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001425 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001426 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001427 if submodules:
1428 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001429 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001430 syncbuf.fail(self, e)
1431 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001432 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001433 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001435 if head == revid:
1436 # No changes; don't do anything further.
1437 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001438 # The copy/linkfile config may have changed.
1439 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001440 return
1441
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001442 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001444 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001446 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001447 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001448 syncbuf.info(self,
1449 "leaving %s; does not track upstream",
1450 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001451 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001452 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001453 if submodules:
1454 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001455 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001456 syncbuf.fail(self, e)
1457 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001458 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001459 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001460
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001461 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001462 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001463 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001464 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465 if not_merged:
1466 if upstream_gain:
1467 # The user has published this branch and some of those
1468 # commits are not yet merged upstream. We do not want
1469 # to rewrite the published commits so we punt.
1470 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001471 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001472 "branch %s is published (but not merged) and is now "
1473 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001474 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001475 elif pub == head:
1476 # All published commits are merged, and thus we are a
1477 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001478 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001479 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001480 if submodules:
1481 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001482 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001483
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001484 # Examine the local commits not in the remote. Find the
1485 # last one attributed to this user, if any.
1486 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001487 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001488 last_mine = None
1489 cnt_mine = 0
1490 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301491 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001492 if committer_email == self.UserEmail:
1493 last_mine = commit_id
1494 cnt_mine += 1
1495
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001496 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001497 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001498
1499 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001500 syncbuf.fail(self, _DirtyError())
1501 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001503 # If the upstream switched on us, warn the user.
1504 #
1505 if branch.merge != self.revisionExpr:
1506 if branch.merge and self.revisionExpr:
1507 syncbuf.info(self,
1508 'manifest switched %s...%s',
1509 branch.merge,
1510 self.revisionExpr)
1511 elif branch.merge:
1512 syncbuf.info(self,
1513 'manifest no longer tracks %s',
1514 branch.merge)
1515
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001516 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001517 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001518 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001520 syncbuf.info(self,
1521 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001522 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001524 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001525 if not ID_RE.match(self.revisionExpr):
1526 # in case of manifest sync the revisionExpr might be a SHA1
1527 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001528 if not branch.merge.startswith('refs/'):
1529 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001530 branch.Save()
1531
Mike Pontillod3153822012-02-28 11:53:24 -08001532 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001533 def _docopyandlink():
1534 self._CopyAndLinkFiles()
1535
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001536 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001537 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001538 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001539 if submodules:
1540 syncbuf.later2(self, _dosubmodules)
1541 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001542 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001543 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001544 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001545 if submodules:
1546 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001547 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001548 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001549 syncbuf.fail(self, e)
1550 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001551 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001552 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001553 if submodules:
1554 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001556 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557 # dest should already be an absolute path, but src is project relative
1558 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001559 abssrc = os.path.join(self.worktree, src)
1560 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001561
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001562 def AddLinkFile(self, src, dest, absdest):
1563 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001564 # make src relative path to dest
1565 absdestdir = os.path.dirname(absdest)
1566 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001567 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001568
James W. Mills24c13082012-04-12 15:04:13 -05001569 def AddAnnotation(self, name, value, keep):
1570 self.annotations.append(_Annotation(name, value, keep))
1571
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001572 def DownloadPatchSet(self, change_id, patch_id):
1573 """Download a single patch set of a single change to FETCH_HEAD.
1574 """
1575 remote = self.GetRemote(self.remote.name)
1576
1577 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001578 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001579 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001580 if GitCommand(self, cmd, bare=True).Wait() != 0:
1581 return None
1582 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001583 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001584 change_id,
1585 patch_id,
1586 self.bare_git.rev_parse('FETCH_HEAD'))
1587
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001588
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001589# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590
Simran Basib9a1b732015-08-20 12:19:28 -07001591 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001592 """Create a new branch off the manifest's revision.
1593 """
Simran Basib9a1b732015-08-20 12:19:28 -07001594 if not branch_merge:
1595 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001596 head = self.work_git.GetHead()
1597 if head == (R_HEADS + name):
1598 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001599
David Pursehouse8a68ff92012-09-24 12:15:13 +09001600 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001601 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001602 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001603 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001604 capture_stdout=True,
1605 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001606
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001607 branch = self.GetBranch(name)
1608 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001609 branch.merge = branch_merge
1610 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1611 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001612 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001613
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001614 if head.startswith(R_HEADS):
1615 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001616 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001617 except KeyError:
1618 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001619 if revid and head and revid == head:
1620 ref = os.path.join(self.gitdir, R_HEADS + name)
1621 try:
1622 os.makedirs(os.path.dirname(ref))
1623 except OSError:
1624 pass
1625 _lwrite(ref, '%s\n' % revid)
1626 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1627 'ref: %s%s\n' % (R_HEADS, name))
1628 branch.Save()
1629 return True
1630
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001631 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001632 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001633 capture_stdout=True,
1634 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001635 branch.Save()
1636 return True
1637 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638
Wink Saville02d79452009-04-10 13:01:24 -07001639 def CheckoutBranch(self, name):
1640 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001641
1642 Args:
1643 name: The name of the branch to checkout.
1644
1645 Returns:
1646 True if the checkout succeeded; False if it didn't; None if the branch
1647 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001648 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001649 rev = R_HEADS + name
1650 head = self.work_git.GetHead()
1651 if head == rev:
1652 # Already on the branch
1653 #
1654 return True
Wink Saville02d79452009-04-10 13:01:24 -07001655
David Pursehouse8a68ff92012-09-24 12:15:13 +09001656 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001657 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001658 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001659 except KeyError:
1660 # Branch does not exist in this project
1661 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001662 return None
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001663
1664 if head.startswith(R_HEADS):
1665 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001667 except KeyError:
1668 head = None
1669
1670 if head == revid:
1671 # Same revision; just update HEAD to point to the new
1672 # target branch, but otherwise take no other action.
1673 #
1674 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1675 'ref: %s%s\n' % (R_HEADS, name))
1676 return True
Wink Saville02d79452009-04-10 13:01:24 -07001677
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001678 return GitCommand(self,
1679 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001680 capture_stdout=True,
1681 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001682
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001683 def AbandonBranch(self, name):
1684 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001685
1686 Args:
1687 name: The name of the branch to abandon.
1688
1689 Returns:
1690 True if the abandon succeeded; False if it didn't; None if the branch
1691 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001692 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001693 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001694 all_refs = self.bare_ref.all
1695 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001696 # Doesn't exist
1697 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001698
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001699 head = self.work_git.GetHead()
1700 if head == rev:
1701 # We can't destroy the branch while we are sitting
1702 # on it. Switch to a detached HEAD.
1703 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001704 head = all_refs[head]
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001705
David Pursehouse8a68ff92012-09-24 12:15:13 +09001706 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001707 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001708 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1709 '%s\n' % revid)
1710 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001711 self._Checkout(revid, quiet=True)
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001712
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001713 return GitCommand(self,
1714 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001715 capture_stdout=True,
1716 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001717
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001718 def PruneHeads(self):
1719 """Prune any topic branches already merged into upstream.
1720 """
1721 cb = self.CurrentBranch
1722 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001723 left = self._allrefs
1724 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001725 if name.startswith(R_HEADS):
1726 name = name[len(R_HEADS):]
1727 if cb is None or name != cb:
1728 kill.append(name)
1729
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001730 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001731 if cb is not None \
1732 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001733 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001734 self.work_git.DetachHead(HEAD)
1735 kill.append(cb)
1736
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001737 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001738 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001739
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001740 try:
1741 self.bare_git.DetachHead(rev)
1742
1743 b = ['branch', '-d']
1744 b.extend(kill)
1745 b = GitCommand(self, b, bare=True,
1746 capture_stdout=True,
1747 capture_stderr=True)
1748 b.Wait()
1749 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001750 if ID_RE.match(old):
1751 self.bare_git.DetachHead(old)
1752 else:
1753 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001754 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001755
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001756 for branch in kill:
1757 if (R_HEADS + branch) not in left:
1758 self.CleanPublishedCache()
1759 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001760
1761 if cb and cb not in kill:
1762 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001763 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001764
1765 kept = []
1766 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001767 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001768 branch = self.GetBranch(branch)
1769 base = branch.LocalMerge
1770 if not base:
1771 base = rev
1772 kept.append(ReviewableBranch(self, branch, base))
1773 return kept
1774
1775
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001776# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001777
1778 def GetRegisteredSubprojects(self):
1779 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001780
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001781 def rec(subprojects):
1782 if not subprojects:
1783 return
1784 result.extend(subprojects)
1785 for p in subprojects:
1786 rec(p.subprojects)
1787 rec(self.subprojects)
1788 return result
1789
1790 def _GetSubmodules(self):
1791 # Unfortunately we cannot call `git submodule status --recursive` here
1792 # because the working tree might not exist yet, and it cannot be used
1793 # without a working tree in its current implementation.
1794
1795 def get_submodules(gitdir, rev):
1796 # Parse .gitmodules for submodule sub_paths and sub_urls
1797 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1798 if not sub_paths:
1799 return []
1800 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1801 # revision of submodule repository
1802 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1803 submodules = []
1804 for sub_path, sub_url in zip(sub_paths, sub_urls):
1805 try:
1806 sub_rev = sub_revs[sub_path]
1807 except KeyError:
1808 # Ignore non-exist submodules
1809 continue
1810 submodules.append((sub_rev, sub_path, sub_url))
1811 return submodules
1812
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001813 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1814 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001815
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001816 def parse_gitmodules(gitdir, rev):
1817 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1818 try:
Anthony King7bdac712014-07-16 12:56:40 +01001819 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1820 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001821 except GitError:
1822 return [], []
1823 if p.Wait() != 0:
1824 return [], []
1825
1826 gitmodules_lines = []
1827 fd, temp_gitmodules_path = tempfile.mkstemp()
1828 try:
1829 os.write(fd, p.stdout)
1830 os.close(fd)
1831 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001832 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1833 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001834 if p.Wait() != 0:
1835 return [], []
1836 gitmodules_lines = p.stdout.split('\n')
1837 except GitError:
1838 return [], []
1839 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001840 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001841
1842 names = set()
1843 paths = {}
1844 urls = {}
1845 for line in gitmodules_lines:
1846 if not line:
1847 continue
1848 m = re_path.match(line)
1849 if m:
1850 names.add(m.group(1))
1851 paths[m.group(1)] = m.group(2)
1852 continue
1853 m = re_url.match(line)
1854 if m:
1855 names.add(m.group(1))
1856 urls[m.group(1)] = m.group(2)
1857 continue
1858 names = sorted(names)
1859 return ([paths.get(name, '') for name in names],
1860 [urls.get(name, '') for name in names])
1861
1862 def git_ls_tree(gitdir, rev, paths):
1863 cmd = ['ls-tree', rev, '--']
1864 cmd.extend(paths)
1865 try:
Anthony King7bdac712014-07-16 12:56:40 +01001866 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1867 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001868 except GitError:
1869 return []
1870 if p.Wait() != 0:
1871 return []
1872 objects = {}
1873 for line in p.stdout.split('\n'):
1874 if not line.strip():
1875 continue
1876 object_rev, object_path = line.split()[2:4]
1877 objects[object_path] = object_rev
1878 return objects
1879
1880 try:
1881 rev = self.GetRevisionId()
1882 except GitError:
1883 return []
1884 return get_submodules(self.gitdir, rev)
1885
1886 def GetDerivedSubprojects(self):
1887 result = []
1888 if not self.Exists:
1889 # If git repo does not exist yet, querying its submodules will
1890 # mess up its states; so return here.
1891 return result
1892 for rev, path, url in self._GetSubmodules():
1893 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001894 relpath, worktree, gitdir, objdir = \
1895 self.manifest.GetSubprojectPaths(self, name, path)
1896 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001897 if project:
1898 result.extend(project.GetDerivedSubprojects())
1899 continue
David James8d201162013-10-11 17:03:19 -07001900
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001901 if url.startswith('..'):
1902 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001903 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001904 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001905 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001906 review=self.remote.review,
1907 revision=self.remote.revision)
1908 subproject = Project(manifest=self.manifest,
1909 name=name,
1910 remote=remote,
1911 gitdir=gitdir,
1912 objdir=objdir,
1913 worktree=worktree,
1914 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001915 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001916 revisionId=rev,
1917 rebase=self.rebase,
1918 groups=self.groups,
1919 sync_c=self.sync_c,
1920 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001921 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001922 parent=self,
1923 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001924 result.append(subproject)
1925 result.extend(subproject.GetDerivedSubprojects())
1926 return result
1927
1928
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001929# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001930 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001931 try:
1932 # if revision (sha or tag) is not present then following function
1933 # throws an error.
1934 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1935 return True
1936 except GitError:
1937 # There is no such persistent revision. We have to fetch it.
1938 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001939
Julien Campergue335f5ef2013-10-16 11:02:35 +02001940 def _FetchArchive(self, tarpath, cwd=None):
1941 cmd = ['archive', '-v', '-o', tarpath]
1942 cmd.append('--remote=%s' % self.remote.url)
1943 cmd.append('--prefix=%s/' % self.relpath)
1944 cmd.append(self.revisionExpr)
1945
1946 command = GitCommand(self, cmd, cwd=cwd,
1947 capture_stdout=True,
1948 capture_stderr=True)
1949
1950 if command.Wait() != 0:
1951 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1952
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001953 def _RemoteFetch(self, name=None,
1954 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001955 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001956 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001957 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001958 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001959 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001960 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001961 submodules=False,
1962 force_sync=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001963
1964 is_sha1 = False
1965 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001966 # The depth should not be used when fetching to a mirror because
1967 # it will result in a shallow repository that cannot be cloned or
1968 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001969 # The repo project should also never be synced with partial depth.
1970 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1971 depth = None
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001972
Shawn Pearce69e04d82014-01-29 12:48:54 -08001973 if depth:
1974 current_branch_only = True
1975
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001976 if ID_RE.match(self.revisionExpr) is not None:
1977 is_sha1 = True
1978
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001979 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001980 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001981 # this is a tag and its sha1 value should never change
1982 tag_name = self.revisionExpr[len(R_TAGS):]
1983
1984 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001985 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001986 if not quiet:
1987 print('Skipped fetching project %s (already have persistent ref)'
1988 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001989 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001990 if is_sha1 and not depth:
1991 # When syncing a specific commit and --depth is not set:
1992 # * if upstream is explicitly specified and is not a sha1, fetch only
1993 # upstream as users expect only upstream to be fetch.
1994 # Note: The commit might not be in upstream in which case the sync
1995 # will fail.
1996 # * otherwise, fetch all branches to make sure we end up with the
1997 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001998 if self.upstream:
1999 current_branch_only = not ID_RE.match(self.upstream)
2000 else:
2001 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002002
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002003 if not name:
2004 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002005
2006 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002007 remote = self.GetRemote(name)
2008 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002009 ssh_proxy = True
2010
Shawn O. Pearce88443382010-10-08 10:02:09 +02002011 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002012 if alt_dir and 'objects' == os.path.basename(alt_dir):
2013 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2015 remote = self.GetRemote(name)
2016
David Pursehouse8a68ff92012-09-24 12:15:13 +09002017 all_refs = self.bare_ref.all
2018 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 tmp = set()
2020
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302021 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002022 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002023 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002024 all_refs[r] = ref_id
2025 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002026 continue
2027
David Pursehouse8a68ff92012-09-24 12:15:13 +09002028 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002029 continue
2030
David Pursehouse8a68ff92012-09-24 12:15:13 +09002031 r = 'refs/_alt/%s' % ref_id
2032 all_refs[r] = ref_id
2033 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034 tmp.add(r)
2035
heping3d7bbc92017-04-12 19:51:47 +08002036 tmp_packed_lines = []
2037 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302039 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002040 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002041 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002042 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002043 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002044
heping3d7bbc92017-04-12 19:51:47 +08002045 tmp_packed = ''.join(tmp_packed_lines)
2046 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002047 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002048 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002049 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002051 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002052
Conley Owensf97e8382015-01-21 11:12:46 -08002053 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002054 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002055 else:
2056 # If this repo has shallow objects, then we don't know which refs have
2057 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2058 # do this with projects that don't have shallow objects, since it is less
2059 # efficient.
2060 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2061 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002062
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002063 if quiet:
2064 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002065 if not self.worktree:
2066 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002067 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002068
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002069 # If using depth then we should not get all the tags since they may
2070 # be outside of the depth.
2071 if no_tags or depth:
2072 cmd.append('--no-tags')
2073 else:
2074 cmd.append('--tags')
2075
Mike Frysingere57f1142019-03-18 21:27:54 -04002076 if force_sync:
2077 cmd.append('--force')
2078
David Pursehouse74cfd272015-10-14 10:50:15 +09002079 if prune:
2080 cmd.append('--prune')
2081
Martin Kellye4e94d22017-03-21 16:05:12 -07002082 if submodules:
2083 cmd.append('--recurse-submodules=on-demand')
2084
Conley Owens80b87fe2014-05-09 17:13:44 -07002085 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002086 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002088 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002089 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002090 spec.append('tag')
2091 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002092
David Pursehouse403b64e2015-04-27 10:41:33 +09002093 if not self.manifest.IsMirror:
2094 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002095 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002096 # Shallow checkout of a specific commit, fetch from that commit and not
2097 # the heads only as the commit might be deeper in the history.
2098 spec.append(branch)
2099 else:
2100 if is_sha1:
2101 branch = self.upstream
2102 if branch is not None and branch.strip():
2103 if not branch.startswith('refs/'):
2104 branch = R_HEADS + branch
2105 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002106 cmd.extend(spec)
2107
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002108 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002109 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002110 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002111 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002112 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002113 ok = True
2114 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002115 # If needed, run the 'git remote prune' the first time through the loop
2116 elif (not _i and
2117 "error:" in gitcmd.stderr and
2118 "git remote prune" in gitcmd.stderr):
2119 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002120 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002121 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002122 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002123 break
2124 continue
Brian Harring14a66742012-09-28 20:21:57 -07002125 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002126 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2127 # in sha1 mode, we just tried sync'ing from the upstream field; it
2128 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002129 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002130 elif ret < 0:
2131 # Git died with a signal, exit immediately
2132 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002133 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134
2135 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002136 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002137 if old_packed != '':
2138 _lwrite(packed_refs, old_packed)
2139 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002140 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002141 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002142
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002143 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002144 # We just synced the upstream given branch; verify we
2145 # got what we wanted, else trigger a second run of all
2146 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002147 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002148 if current_branch_only and depth:
2149 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002150 return self._RemoteFetch(name=name,
2151 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002152 initial=False, quiet=quiet, alt_dir=alt_dir,
2153 depth=None)
2154 else:
2155 # Avoid infinite recursion: sync all branches with depth set to None
2156 return self._RemoteFetch(name=name, current_branch_only=False,
2157 initial=False, quiet=quiet, alt_dir=alt_dir,
2158 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002159
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002160 return ok
2161
2162 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002163 if initial and \
2164 (self.manifest.manifestProject.config.GetString('repo.depth') or
2165 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002166 return False
2167
2168 remote = self.GetRemote(self.remote.name)
2169 bundle_url = remote.url + '/clone.bundle'
2170 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002171 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2172 'persistent-http',
2173 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002174 return False
2175
2176 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2177 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002178
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002179 exist_dst = os.path.exists(bundle_dst)
2180 exist_tmp = os.path.exists(bundle_tmp)
2181
2182 if not initial and not exist_dst and not exist_tmp:
2183 return False
2184
2185 if not exist_dst:
2186 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2187 if not exist_dst:
2188 return False
2189
2190 cmd = ['fetch']
2191 if quiet:
2192 cmd.append('--quiet')
2193 if not self.worktree:
2194 cmd.append('--update-head-ok')
2195 cmd.append(bundle_dst)
2196 for f in remote.fetch:
2197 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002198 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002199
2200 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002201 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002202 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002203 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002204 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002205 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002206
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002207 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002208 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002209 platform_utils.remove(dstPath)
Shawn O. Pearce29472462011-10-11 09:24:07 -07002210
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002211 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002212 if quiet:
2213 cmd += ['--silent']
2214 if os.path.exists(tmpPath):
2215 size = os.stat(tmpPath).st_size
2216 if size >= 1024:
2217 cmd += ['--continue-at', '%d' % (size,)]
2218 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002219 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002220 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2221 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002222 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002223 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002224 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002225 if srcUrl.startswith('persistent-'):
2226 srcUrl = srcUrl[len('persistent-'):]
2227 cmd += [srcUrl]
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002228
Dave Borowitz137d0132015-01-02 11:12:54 -08002229 if IsTrace():
2230 Trace('%s', ' '.join(cmd))
2231 try:
2232 proc = subprocess.Popen(cmd)
2233 except OSError:
2234 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002235
Dave Borowitz137d0132015-01-02 11:12:54 -08002236 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002237
Dave Borowitz137d0132015-01-02 11:12:54 -08002238 if curlret == 22:
2239 # From curl man page:
2240 # 22: HTTP page not retrieved. The requested url was not found or
2241 # returned another error with the HTTP error code being 400 or above.
2242 # This return code only appears if -f, --fail is used.
2243 if not quiet:
2244 print("Server does not provide clone.bundle; ignoring.",
2245 file=sys.stderr)
2246 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002247
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002248 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002249 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002250 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002251 return True
2252 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002253 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002254 return False
2255 else:
2256 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002257
Kris Giesingc8d882a2014-12-23 13:02:32 -08002258 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002259 try:
2260 with open(path) as f:
2261 if f.read(16) == '# v2 git bundle\n':
2262 return True
2263 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002264 if not quiet:
2265 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002266 return False
2267 except OSError:
2268 return False
2269
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002270 def _Checkout(self, rev, quiet=False):
2271 cmd = ['checkout']
2272 if quiet:
2273 cmd.append('-q')
2274 cmd.append(rev)
2275 cmd.append('--')
2276 if GitCommand(self, cmd).Wait() != 0:
2277 if self._allrefs:
2278 raise GitError('%s checkout %s ' % (self.name, rev))
2279
Anthony King7bdac712014-07-16 12:56:40 +01002280 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002281 cmd = ['cherry-pick']
2282 cmd.append(rev)
2283 cmd.append('--')
2284 if GitCommand(self, cmd).Wait() != 0:
2285 if self._allrefs:
2286 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2287
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302288 def _LsRemote(self, refs):
2289 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302290 p = GitCommand(self, cmd, capture_stdout=True)
2291 if p.Wait() == 0:
2292 if hasattr(p.stdout, 'decode'):
2293 return p.stdout.decode('utf-8')
2294 else:
2295 return p.stdout
2296 return None
2297
Anthony King7bdac712014-07-16 12:56:40 +01002298 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002299 cmd = ['revert']
2300 cmd.append('--no-edit')
2301 cmd.append(rev)
2302 cmd.append('--')
2303 if GitCommand(self, cmd).Wait() != 0:
2304 if self._allrefs:
2305 raise GitError('%s revert %s ' % (self.name, rev))
2306
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002307 def _ResetHard(self, rev, quiet=True):
2308 cmd = ['reset', '--hard']
2309 if quiet:
2310 cmd.append('-q')
2311 cmd.append(rev)
2312 if GitCommand(self, cmd).Wait() != 0:
2313 raise GitError('%s reset --hard %s ' % (self.name, rev))
2314
Martin Kellye4e94d22017-03-21 16:05:12 -07002315 def _SyncSubmodules(self, quiet=True):
2316 cmd = ['submodule', 'update', '--init', '--recursive']
2317 if quiet:
2318 cmd.append('-q')
2319 if GitCommand(self, cmd).Wait() != 0:
2320 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2321
Anthony King7bdac712014-07-16 12:56:40 +01002322 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002323 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002324 if onto is not None:
2325 cmd.extend(['--onto', onto])
2326 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002327 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002328 raise GitError('%s rebase %s ' % (self.name, upstream))
2329
Pierre Tardy3d125942012-05-04 12:18:12 +02002330 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002331 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002332 if ffonly:
2333 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002334 if GitCommand(self, cmd).Wait() != 0:
2335 raise GitError('%s merge %s ' % (self.name, head))
2336
Kevin Degiabaa7f32014-11-12 11:27:45 -07002337 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002338 init_git_dir = not os.path.exists(self.gitdir)
2339 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002340 try:
2341 # Initialize the bare repository, which contains all of the objects.
2342 if init_obj_dir:
2343 os.makedirs(self.objdir)
2344 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002345
Kevin Degib1a07b82015-07-27 13:33:43 -06002346 # If we have a separate directory to hold refs, initialize it as well.
2347 if self.objdir != self.gitdir:
2348 if init_git_dir:
2349 os.makedirs(self.gitdir)
Kevin Degi384b3c52014-10-16 16:02:58 -06002350
Kevin Degib1a07b82015-07-27 13:33:43 -06002351 if init_obj_dir or init_git_dir:
2352 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2353 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002354 try:
2355 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2356 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002357 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002358 print("Retrying clone after deleting %s" %
2359 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002360 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002361 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2362 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002363 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002364 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002365 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2366 except:
2367 raise e
2368 raise e
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002369
Kevin Degib1a07b82015-07-27 13:33:43 -06002370 if init_git_dir:
2371 mp = self.manifest.manifestProject
2372 ref_dir = mp.config.GetString('repo.reference') or ''
Shawn O. Pearce88443382010-10-08 10:02:09 +02002373
Kevin Degib1a07b82015-07-27 13:33:43 -06002374 if ref_dir or mirror_git:
2375 if not mirror_git:
2376 mirror_git = os.path.join(ref_dir, self.name + '.git')
2377 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2378 self.relpath + '.git')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002379
Kevin Degib1a07b82015-07-27 13:33:43 -06002380 if os.path.exists(mirror_git):
2381 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002382
Kevin Degib1a07b82015-07-27 13:33:43 -06002383 elif os.path.exists(repo_git):
2384 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002385
Kevin Degib1a07b82015-07-27 13:33:43 -06002386 else:
2387 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002388
Kevin Degib1a07b82015-07-27 13:33:43 -06002389 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002390 if not os.path.isabs(ref_dir):
2391 # The alternate directory is relative to the object database.
2392 ref_dir = os.path.relpath(ref_dir,
2393 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002394 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2395 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002396
Kevin Degib1a07b82015-07-27 13:33:43 -06002397 self._UpdateHooks()
Jimmie Westera0444582012-10-24 13:44:42 +02002398
Kevin Degib1a07b82015-07-27 13:33:43 -06002399 m = self.manifest.manifestProject.config
2400 for key in ['user.name', 'user.email']:
2401 if m.Has(key, include_defaults=False):
2402 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002403 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002404 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002405 if self.manifest.IsMirror:
2406 self.config.SetString('core.bare', 'true')
2407 else:
2408 self.config.SetString('core.bare', None)
2409 except Exception:
2410 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002411 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002412 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002413 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002414 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002415
Jimmie Westera0444582012-10-24 13:44:42 +02002416 def _UpdateHooks(self):
2417 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002418 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002419
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002420 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002421 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002422 if not os.path.exists(hooks):
2423 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002424 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002425 name = os.path.basename(stock_hook)
2426
Victor Boivie65e0f352011-04-18 11:23:29 +02002427 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002428 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002429 # Don't install a Gerrit Code Review hook if this
2430 # project does not appear to use it for reviews.
2431 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002432 # Since the manifest project is one of those, but also
2433 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002434 continue
2435
2436 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002437 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002438 continue
2439 if os.path.exists(dst):
2440 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002441 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002442 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002443 _warn("%s: Not replacing locally modified %s hook",
2444 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002445 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002446 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002447 platform_utils.symlink(
2448 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002449 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002450 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002451 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002452 else:
2453 raise
2454
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002455 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002456 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002457 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002458 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002459 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002460 remote.review = self.remote.review
2461 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002462
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002463 if self.worktree:
2464 remote.ResetFetch(mirror=False)
2465 else:
2466 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002467 remote.Save()
2468
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002469 def _InitMRef(self):
2470 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002471 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002472
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002473 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002474 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002475
2476 def _InitAnyMRef(self, ref):
2477 cur = self.bare_ref.symref(ref)
2478
2479 if self.revisionId:
2480 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2481 msg = 'manifest set to %s' % self.revisionId
2482 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002483 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002484 else:
2485 remote = self.GetRemote(self.remote.name)
2486 dst = remote.ToLocal(self.revisionExpr)
2487 if cur != dst:
2488 msg = 'manifest set to %s' % self.revisionExpr
2489 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002490
Kevin Degi384b3c52014-10-16 16:02:58 -06002491 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002492 symlink_files = self.shareable_files[:]
2493 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002494 if share_refs:
2495 symlink_files += self.working_tree_files
2496 symlink_dirs += self.working_tree_dirs
2497 to_symlink = symlink_files + symlink_dirs
2498 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002499 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002500 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002501 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002502 # Fail if the links are pointing to the wrong place
2503 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002504 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002505 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002506 'work tree. If you\'re comfortable with the '
2507 'possibility of losing the work tree\'s git metadata,'
2508 ' use `repo sync --force-sync {0}` to '
2509 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002510
David James8d201162013-10-11 17:03:19 -07002511 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2512 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2513
2514 Args:
2515 gitdir: The bare git repository. Must already be initialized.
2516 dotgit: The repository you would like to initialize.
2517 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2518 Only one work tree can store refs under a given |gitdir|.
2519 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2520 This saves you the effort of initializing |dotgit| yourself.
2521 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002522 symlink_files = self.shareable_files[:]
2523 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002524 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002525 symlink_files += self.working_tree_files
2526 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002527 to_symlink = symlink_files + symlink_dirs
2528
2529 to_copy = []
2530 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002531 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002532
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002533 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002534 for name in set(to_copy).union(to_symlink):
2535 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002536 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002537 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002538
Kevin Degi384b3c52014-10-16 16:02:58 -06002539 if os.path.lexists(dst):
2540 continue
David James8d201162013-10-11 17:03:19 -07002541
2542 # If the source dir doesn't exist, create an empty dir.
2543 if name in symlink_dirs and not os.path.lexists(src):
2544 os.makedirs(src)
2545
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002546 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002547 platform_utils.symlink(
2548 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002549 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002550 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002551 shutil.copytree(src, dst)
2552 elif os.path.isfile(src):
2553 shutil.copy(src, dst)
2554
Conley Owens80b87fe2014-05-09 17:13:44 -07002555 # If the source file doesn't exist, ensure the destination
2556 # file doesn't either.
2557 if name in symlink_files and not os.path.lexists(src):
2558 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002559 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002560 except OSError:
2561 pass
2562
David James8d201162013-10-11 17:03:19 -07002563 except OSError as e:
2564 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002565 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002566 else:
2567 raise
2568
Martin Kellye4e94d22017-03-21 16:05:12 -07002569 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002570 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002571 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002572 try:
2573 if init_dotgit:
2574 os.makedirs(dotgit)
2575 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2576 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002577
Kevin Degiabaa7f32014-11-12 11:27:45 -07002578 try:
2579 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2580 except GitError as e:
2581 if force_sync:
2582 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002583 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002584 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002585 except:
2586 raise e
2587 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002588
Kevin Degib1a07b82015-07-27 13:33:43 -06002589 if init_dotgit:
2590 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002591
Kevin Degib1a07b82015-07-27 13:33:43 -06002592 cmd = ['read-tree', '--reset', '-u']
2593 cmd.append('-v')
2594 cmd.append(HEAD)
2595 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002596 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002597
Martin Kellye4e94d22017-03-21 16:05:12 -07002598 if submodules:
2599 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002600 self._CopyAndLinkFiles()
2601 except Exception:
2602 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002603 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002604 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002605
Renaud Paquay788e9622017-01-27 11:41:12 -08002606 def _get_symlink_error_message(self):
2607 if platform_utils.isWindows():
2608 return ('Unable to create symbolic link. Please re-run the command as '
2609 'Administrator, or see '
2610 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2611 'for other options.')
2612 return 'filesystem must support symlinks'
2613
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002614 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002615 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002616
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002617 def _revlist(self, *args, **kw):
2618 a = []
2619 a.extend(args)
2620 a.append('--')
2621 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002622
2623 @property
2624 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002625 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002626
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002627 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002628 """Get logs between two revisions of this project."""
2629 comp = '..'
2630 if rev1:
2631 revs = [rev1]
2632 if rev2:
2633 revs.extend([comp, rev2])
2634 cmd = ['log', ''.join(revs)]
2635 out = DiffColoring(self.config)
2636 if out.is_on and color:
2637 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002638 if pretty_format is not None:
2639 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002640 if oneline:
2641 cmd.append('--oneline')
2642
2643 try:
2644 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2645 if log.Wait() == 0:
2646 return log.stdout
2647 except GitError:
2648 # worktree may not exist if groups changed for example. In that case,
2649 # try in gitdir instead.
2650 if not os.path.exists(self.worktree):
2651 return self.bare_git.log(*cmd[1:])
2652 else:
2653 raise
2654 return None
2655
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002656 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2657 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002658 """Get the list of logs from this revision to given revisionId"""
2659 logs = {}
2660 selfId = self.GetRevisionId(self._allrefs)
2661 toId = toProject.GetRevisionId(toProject._allrefs)
2662
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002663 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2664 pretty_format=pretty_format)
2665 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2666 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002667 return logs
2668
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002669 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002670
David James8d201162013-10-11 17:03:19 -07002671 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002672 self._project = project
2673 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002674 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002675
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002676 def LsOthers(self):
2677 p = GitCommand(self._project,
2678 ['ls-files',
2679 '-z',
2680 '--others',
2681 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002682 bare=False,
David James8d201162013-10-11 17:03:19 -07002683 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002684 capture_stdout=True,
2685 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002686 if p.Wait() == 0:
2687 out = p.stdout
2688 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002689 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002690 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002691 return []
2692
2693 def DiffZ(self, name, *args):
2694 cmd = [name]
2695 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002696 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002697 cmd.extend(args)
2698 p = GitCommand(self._project,
2699 cmd,
David James8d201162013-10-11 17:03:19 -07002700 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002701 bare=False,
2702 capture_stdout=True,
2703 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002704 try:
2705 out = p.process.stdout.read()
2706 r = {}
2707 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002708 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002710 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002711 info = next(out)
2712 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002713 except StopIteration:
2714 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002715
2716 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002717
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002718 def __init__(self, path, omode, nmode, oid, nid, state):
2719 self.path = path
2720 self.src_path = None
2721 self.old_mode = omode
2722 self.new_mode = nmode
2723 self.old_id = oid
2724 self.new_id = nid
2725
2726 if len(state) == 1:
2727 self.status = state
2728 self.level = None
2729 else:
2730 self.status = state[:1]
2731 self.level = state[1:]
2732 while self.level.startswith('0'):
2733 self.level = self.level[1:]
2734
2735 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002736 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002737 if info.status in ('R', 'C'):
2738 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002739 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002740 r[info.path] = info
2741 return r
2742 finally:
2743 p.Wait()
2744
2745 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002746 if self._bare:
2747 path = os.path.join(self._project.gitdir, HEAD)
2748 else:
2749 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002750 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002751 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002752 except IOError as e:
2753 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002754 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002755 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002756 finally:
2757 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302758 try:
2759 line = line.decode()
2760 except AttributeError:
2761 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002762 if line.startswith('ref: '):
2763 return line[5:-1]
2764 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002765
2766 def SetHead(self, ref, message=None):
2767 cmdv = []
2768 if message is not None:
2769 cmdv.extend(['-m', message])
2770 cmdv.append(HEAD)
2771 cmdv.append(ref)
2772 self.symbolic_ref(*cmdv)
2773
2774 def DetachHead(self, new, message=None):
2775 cmdv = ['--no-deref']
2776 if message is not None:
2777 cmdv.extend(['-m', message])
2778 cmdv.append(HEAD)
2779 cmdv.append(new)
2780 self.update_ref(*cmdv)
2781
2782 def UpdateRef(self, name, new, old=None,
2783 message=None,
2784 detach=False):
2785 cmdv = []
2786 if message is not None:
2787 cmdv.extend(['-m', message])
2788 if detach:
2789 cmdv.append('--no-deref')
2790 cmdv.append(name)
2791 cmdv.append(new)
2792 if old is not None:
2793 cmdv.append(old)
2794 self.update_ref(*cmdv)
2795
2796 def DeleteRef(self, name, old=None):
2797 if not old:
2798 old = self.rev_parse(name)
2799 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002800 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002801
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002802 def rev_list(self, *args, **kw):
2803 if 'format' in kw:
2804 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2805 else:
2806 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807 cmdv.extend(args)
2808 p = GitCommand(self._project,
2809 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002810 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002811 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002812 capture_stdout=True,
2813 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814 r = []
2815 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002816 if line[-1] == '\n':
2817 line = line[:-1]
2818 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002819 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002820 raise GitError('%s rev-list %s: %s' %
2821 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002822 return r
2823
2824 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002825 """Allow arbitrary git commands using pythonic syntax.
2826
2827 This allows you to do things like:
2828 git_obj.rev_parse('HEAD')
2829
2830 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2831 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002832 Any other positional arguments will be passed to the git command, and the
2833 following keyword arguments are supported:
2834 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002835
2836 Args:
2837 name: The name of the git command to call. Any '_' characters will
2838 be replaced with '-'.
2839
2840 Returns:
2841 A callable object that will try to call git with the named command.
2842 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002843 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002844
Dave Borowitz091f8932012-10-23 17:01:04 -07002845 def runner(*args, **kwargs):
2846 cmdv = []
2847 config = kwargs.pop('config', None)
2848 for k in kwargs:
2849 raise TypeError('%s() got an unexpected keyword argument %r'
2850 % (name, k))
2851 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002852 if not git_require((1, 7, 2)):
2853 raise ValueError('cannot set config on command line for %s()'
2854 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302855 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002856 cmdv.append('-c')
2857 cmdv.append('%s=%s' % (k, v))
2858 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002859 cmdv.extend(args)
2860 p = GitCommand(self._project,
2861 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002862 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002863 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002864 capture_stdout=True,
2865 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002866 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002867 raise GitError('%s %s: %s' %
2868 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002869 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302870 try:
Conley Owensedd01512013-09-26 12:59:58 -07002871 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302872 except AttributeError:
2873 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002874 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2875 return r[:-1]
2876 return r
2877 return runner
2878
2879
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002880class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002881
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002882 def __str__(self):
2883 return 'prior sync failed; rebase still in progress'
2884
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002885
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002886class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002887
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002888 def __str__(self):
2889 return 'contains uncommitted changes'
2890
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002891
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002892class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002893
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002894 def __init__(self, project, text):
2895 self.project = project
2896 self.text = text
2897
2898 def Print(self, syncbuf):
2899 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2900 syncbuf.out.nl()
2901
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002902
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002903class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002904
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002905 def __init__(self, project, why):
2906 self.project = project
2907 self.why = why
2908
2909 def Print(self, syncbuf):
2910 syncbuf.out.fail('error: %s/: %s',
2911 self.project.relpath,
2912 str(self.why))
2913 syncbuf.out.nl()
2914
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002915
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002916class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002917
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002918 def __init__(self, project, action):
2919 self.project = project
2920 self.action = action
2921
2922 def Run(self, syncbuf):
2923 out = syncbuf.out
2924 out.project('project %s/', self.project.relpath)
2925 out.nl()
2926 try:
2927 self.action()
2928 out.nl()
2929 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002930 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002931 out.nl()
2932 return False
2933
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002934
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002935class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002936
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002937 def __init__(self, config):
2938 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002939 self.project = self.printer('header', attr='bold')
2940 self.info = self.printer('info')
2941 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002942
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002943
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002944class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002945
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002946 def __init__(self, config, detach_head=False):
2947 self._messages = []
2948 self._failures = []
2949 self._later_queue1 = []
2950 self._later_queue2 = []
2951
2952 self.out = _SyncColoring(config)
2953 self.out.redirect(sys.stderr)
2954
2955 self.detach_head = detach_head
2956 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002957 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002958
2959 def info(self, project, fmt, *args):
2960 self._messages.append(_InfoMessage(project, fmt % args))
2961
2962 def fail(self, project, err=None):
2963 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002964 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002965
2966 def later1(self, project, what):
2967 self._later_queue1.append(_Later(project, what))
2968
2969 def later2(self, project, what):
2970 self._later_queue2.append(_Later(project, what))
2971
2972 def Finish(self):
2973 self._PrintMessages()
2974 self._RunLater()
2975 self._PrintMessages()
2976 return self.clean
2977
David Rileye0684ad2017-04-05 00:02:59 -07002978 def Recently(self):
2979 recent_clean = self.recent_clean
2980 self.recent_clean = True
2981 return recent_clean
2982
2983 def _MarkUnclean(self):
2984 self.clean = False
2985 self.recent_clean = False
2986
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002987 def _RunLater(self):
2988 for q in ['_later_queue1', '_later_queue2']:
2989 if not self._RunQueue(q):
2990 return
2991
2992 def _RunQueue(self, queue):
2993 for m in getattr(self, queue):
2994 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002995 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002996 return False
2997 setattr(self, queue, [])
2998 return True
2999
3000 def _PrintMessages(self):
3001 for m in self._messages:
3002 m.Print(self)
3003 for m in self._failures:
3004 m.Print(self)
3005
3006 self._messages = []
3007 self._failures = []
3008
3009
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003010class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003011
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003012 """A special project housed under .repo.
3013 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003014
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003015 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003016 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003017 manifest=manifest,
3018 name=name,
3019 gitdir=gitdir,
3020 objdir=gitdir,
3021 worktree=worktree,
3022 remote=RemoteSpec('origin'),
3023 relpath='.repo/%s' % name,
3024 revisionExpr='refs/heads/master',
3025 revisionId=None,
3026 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003027
3028 def PreSync(self):
3029 if self.Exists:
3030 cb = self.CurrentBranch
3031 if cb:
3032 base = self.GetBranch(cb).merge
3033 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003034 self.revisionExpr = base
3035 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003036
Martin Kelly224a31a2017-07-10 14:46:25 -07003037 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003038 """ Prepare MetaProject for manifest branch switch
3039 """
3040
3041 # detach and delete manifest branch, allowing a new
3042 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003043 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003044 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003045 syncbuf.Finish()
3046
3047 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003048 ['update-ref', '-d', 'refs/heads/default'],
3049 capture_stdout=True,
3050 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003051
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003052 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003053 def LastFetch(self):
3054 try:
3055 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3056 return os.path.getmtime(fh)
3057 except OSError:
3058 return 0
3059
3060 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003061 def HasChanges(self):
3062 """Has the remote received new commits not yet checked out?
3063 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003064 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003065 return False
3066
David Pursehouse8a68ff92012-09-24 12:15:13 +09003067 all_refs = self.bare_ref.all
3068 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003069 head = self.work_git.GetHead()
3070 if head.startswith(R_HEADS):
3071 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003072 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003073 except KeyError:
3074 head = None
3075
3076 if revid == head:
3077 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003078 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003079 return True
3080 return False