blob: 9702e9da7cf0200bef6d1a7a7435e26a8635e008 [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.
470
471 Raises:
472 HookError: Raised if the user doesn't approve and abort_if_user_denies
473 was passed to the consturctor.
474 """
Doug Anderson37282b42011-03-04 11:54:18 -0800475 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400476 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".
Conley Owens971de8e2012-04-16 10:36:08 -0700862
863 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"
Colin Cross5acde752012-03-28 20:15:45 -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)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001167
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001168 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)
1192
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,
Xin Li745be2e2019-06-03 11:24:30 -07001229 submodules=False,
1230 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001231 """Perform only the network IO portion of the sync process.
1232 Local working directory/branch state is not affected.
1233 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001234 if archive and not isinstance(self, MetaProject):
1235 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001236 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001237 return False
1238
1239 name = self.relpath.replace('\\', '/')
1240 name = name.replace('/', '_')
1241 tarpath = '%s.tar' % name
1242 topdir = self.manifest.topdir
1243
1244 try:
1245 self._FetchArchive(tarpath, cwd=topdir)
1246 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001247 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001248 return False
1249
1250 # From now on, we only need absolute tarpath
1251 tarpath = os.path.join(topdir, tarpath)
1252
1253 if not self._ExtractArchive(tarpath, path=topdir):
1254 return False
1255 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001256 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001257 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001258 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001259 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001260 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001261 if is_new is None:
1262 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001263 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001264 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001265 else:
1266 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001267 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001268
1269 if is_new:
1270 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1271 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001272 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001273 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001274 # This works for both absolute and relative alternate directories.
1275 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001276 finally:
1277 fd.close()
1278 except IOError:
1279 alt_dir = None
1280 else:
1281 alt_dir = None
1282
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001283 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001284 and alt_dir is None \
1285 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001286 is_new = False
1287
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001288 if not current_branch_only:
1289 if self.sync_c:
1290 current_branch_only = True
1291 elif not self.manifest._loaded:
1292 # Manifest cannot check defaults until it syncs.
1293 current_branch_only = False
1294 elif self.manifest.default.sync_c:
1295 current_branch_only = True
1296
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001297 if not no_tags:
1298 if not self.sync_tags:
1299 no_tags = True
1300
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001301 if self.clone_depth:
1302 depth = self.clone_depth
1303 else:
1304 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1305
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001306 need_to_fetch = not (optimized_fetch and
1307 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001308 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001309 if (need_to_fetch and
1310 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1311 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001312 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001313 submodules=submodules, force_sync=force_sync,
1314 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001315 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001316
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001317 mp = self.manifest.manifestProject
1318 dissociate = mp.config.GetBoolean('repo.dissociate')
1319 if dissociate:
1320 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1321 if os.path.exists(alternates_file):
1322 cmd = ['repack', '-a', '-d']
1323 if GitCommand(self, cmd, bare=True).Wait() != 0:
1324 return False
1325 platform_utils.remove(alternates_file)
1326
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001327 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001328 self._InitMRef()
1329 else:
1330 self._InitMirrorHead()
1331 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001332 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001333 except OSError:
1334 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001335 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001336
1337 def PostRepoUpgrade(self):
1338 self._InitHooks()
1339
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001340 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001341 if self.manifest.isGitcClient:
1342 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001343 for copyfile in self.copyfiles:
1344 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001345 for linkfile in self.linkfiles:
1346 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001347
Julien Camperguedd654222014-01-09 16:21:37 +01001348 def GetCommitRevisionId(self):
1349 """Get revisionId of a commit.
1350
1351 Use this method instead of GetRevisionId to get the id of the commit rather
1352 than the id of the current git object (for example, a tag)
1353
1354 """
1355 if not self.revisionExpr.startswith(R_TAGS):
1356 return self.GetRevisionId(self._allrefs)
1357
1358 try:
1359 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1360 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001361 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1362 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001363
David Pursehouse8a68ff92012-09-24 12:15:13 +09001364 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001365 if self.revisionId:
1366 return self.revisionId
1367
1368 rem = self.GetRemote(self.remote.name)
1369 rev = rem.ToLocal(self.revisionExpr)
1370
David Pursehouse8a68ff92012-09-24 12:15:13 +09001371 if all_refs is not None and rev in all_refs:
1372 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001373
1374 try:
1375 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1376 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001377 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1378 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001379
Martin Kellye4e94d22017-03-21 16:05:12 -07001380 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001381 """Perform only the local IO portion of the sync process.
1382 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001383 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001384 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001385 all_refs = self.bare_ref.all
1386 self.CleanPublishedCache(all_refs)
1387 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001388
David Pursehouse1d947b32012-10-25 12:23:11 +09001389 def _doff():
1390 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001391 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001392
Martin Kellye4e94d22017-03-21 16:05:12 -07001393 def _dosubmodules():
1394 self._SyncSubmodules(quiet=True)
1395
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001396 head = self.work_git.GetHead()
1397 if head.startswith(R_HEADS):
1398 branch = head[len(R_HEADS):]
1399 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001400 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001401 except KeyError:
1402 head = None
1403 else:
1404 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001406 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001407 # Currently on a detached HEAD. The user is assumed to
1408 # not have any local modifications worth worrying about.
1409 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001410 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001411 syncbuf.fail(self, _PriorSyncFailedError())
1412 return
1413
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001414 if head == revid:
1415 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001416 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001417 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001418 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001419 # The copy/linkfile config may have changed.
1420 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001421 return
1422 else:
1423 lost = self._revlist(not_rev(revid), HEAD)
1424 if lost:
1425 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001426
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001427 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001428 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001429 if submodules:
1430 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001431 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001432 syncbuf.fail(self, e)
1433 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001434 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001435 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001436
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001437 if head == revid:
1438 # No changes; don't do anything further.
1439 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001440 # The copy/linkfile config may have changed.
1441 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001442 return
1443
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001444 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001446 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001447 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001448 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001449 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001450 syncbuf.info(self,
1451 "leaving %s; does not track upstream",
1452 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001453 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001454 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001455 if submodules:
1456 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001457 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001458 syncbuf.fail(self, e)
1459 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001460 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001461 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001462
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001463 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001464 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001466 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001467 if not_merged:
1468 if upstream_gain:
1469 # The user has published this branch and some of those
1470 # commits are not yet merged upstream. We do not want
1471 # to rewrite the published commits so we punt.
1472 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001473 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001474 "branch %s is published (but not merged) and is now "
1475 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001476 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001477 elif pub == head:
1478 # All published commits are merged, and thus we are a
1479 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001480 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001481 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001482 if submodules:
1483 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001484 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001485
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001486 # Examine the local commits not in the remote. Find the
1487 # last one attributed to this user, if any.
1488 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001489 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001490 last_mine = None
1491 cnt_mine = 0
1492 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301493 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001494 if committer_email == self.UserEmail:
1495 last_mine = commit_id
1496 cnt_mine += 1
1497
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001498 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001499 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001500
1501 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001502 syncbuf.fail(self, _DirtyError())
1503 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001504
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001505 # If the upstream switched on us, warn the user.
1506 #
1507 if branch.merge != self.revisionExpr:
1508 if branch.merge and self.revisionExpr:
1509 syncbuf.info(self,
1510 'manifest switched %s...%s',
1511 branch.merge,
1512 self.revisionExpr)
1513 elif branch.merge:
1514 syncbuf.info(self,
1515 'manifest no longer tracks %s',
1516 branch.merge)
1517
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001518 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001520 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001521 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001522 syncbuf.info(self,
1523 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001524 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001525
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001526 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001527 if not ID_RE.match(self.revisionExpr):
1528 # in case of manifest sync the revisionExpr might be a SHA1
1529 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001530 if not branch.merge.startswith('refs/'):
1531 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001532 branch.Save()
1533
Mike Pontillod3153822012-02-28 11:53:24 -08001534 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001535 def _docopyandlink():
1536 self._CopyAndLinkFiles()
1537
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001538 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001539 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001540 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001541 if submodules:
1542 syncbuf.later2(self, _dosubmodules)
1543 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001544 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001545 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001546 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001547 if submodules:
1548 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001549 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001550 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001551 syncbuf.fail(self, e)
1552 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001554 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001555 if submodules:
1556 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001558 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559 # dest should already be an absolute path, but src is project relative
1560 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001561 abssrc = os.path.join(self.worktree, src)
1562 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001563
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001564 def AddLinkFile(self, src, dest, absdest):
1565 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001566 # make src relative path to dest
1567 absdestdir = os.path.dirname(absdest)
1568 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001569 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001570
James W. Mills24c13082012-04-12 15:04:13 -05001571 def AddAnnotation(self, name, value, keep):
1572 self.annotations.append(_Annotation(name, value, keep))
1573
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001574 def DownloadPatchSet(self, change_id, patch_id):
1575 """Download a single patch set of a single change to FETCH_HEAD.
1576 """
1577 remote = self.GetRemote(self.remote.name)
1578
1579 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001580 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001581 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001582 if GitCommand(self, cmd, bare=True).Wait() != 0:
1583 return None
1584 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001585 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001586 change_id,
1587 patch_id,
1588 self.bare_git.rev_parse('FETCH_HEAD'))
1589
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001591# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001592
Simran Basib9a1b732015-08-20 12:19:28 -07001593 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001594 """Create a new branch off the manifest's revision.
1595 """
Simran Basib9a1b732015-08-20 12:19:28 -07001596 if not branch_merge:
1597 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001598 head = self.work_git.GetHead()
1599 if head == (R_HEADS + name):
1600 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001601
David Pursehouse8a68ff92012-09-24 12:15:13 +09001602 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001603 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001604 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001605 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001606 capture_stdout=True,
1607 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001608
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001609 branch = self.GetBranch(name)
1610 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001611 branch.merge = branch_merge
1612 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1613 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001614 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001615
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001616 if head.startswith(R_HEADS):
1617 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001618 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001619 except KeyError:
1620 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001621 if revid and head and revid == head:
1622 ref = os.path.join(self.gitdir, R_HEADS + name)
1623 try:
1624 os.makedirs(os.path.dirname(ref))
1625 except OSError:
1626 pass
1627 _lwrite(ref, '%s\n' % revid)
1628 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1629 'ref: %s%s\n' % (R_HEADS, name))
1630 branch.Save()
1631 return True
1632
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001633 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001634 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001635 capture_stdout=True,
1636 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001637 branch.Save()
1638 return True
1639 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001640
Wink Saville02d79452009-04-10 13:01:24 -07001641 def CheckoutBranch(self, name):
1642 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001643
1644 Args:
1645 name: The name of the branch to checkout.
1646
1647 Returns:
1648 True if the checkout succeeded; False if it didn't; None if the branch
1649 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001650 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001651 rev = R_HEADS + name
1652 head = self.work_git.GetHead()
1653 if head == rev:
1654 # Already on the branch
1655 #
1656 return True
Wink Saville02d79452009-04-10 13:01:24 -07001657
David Pursehouse8a68ff92012-09-24 12:15:13 +09001658 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001659 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001660 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001661 except KeyError:
1662 # Branch does not exist in this project
1663 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001664 return None
Wink Saville02d79452009-04-10 13:01:24 -07001665
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001666 if head.startswith(R_HEADS):
1667 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001668 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001669 except KeyError:
1670 head = None
1671
1672 if head == revid:
1673 # Same revision; just update HEAD to point to the new
1674 # target branch, but otherwise take no other action.
1675 #
1676 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1677 'ref: %s%s\n' % (R_HEADS, name))
1678 return True
1679
1680 return GitCommand(self,
1681 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001682 capture_stdout=True,
1683 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001684
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001685 def AbandonBranch(self, name):
1686 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001687
1688 Args:
1689 name: The name of the branch to abandon.
1690
1691 Returns:
1692 True if the abandon succeeded; False if it didn't; None if the branch
1693 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001694 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001695 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001696 all_refs = self.bare_ref.all
1697 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001698 # Doesn't exist
1699 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001700
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001701 head = self.work_git.GetHead()
1702 if head == rev:
1703 # We can't destroy the branch while we are sitting
1704 # on it. Switch to a detached HEAD.
1705 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001706 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001707
David Pursehouse8a68ff92012-09-24 12:15:13 +09001708 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001709 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001710 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1711 '%s\n' % revid)
1712 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001713 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001714
1715 return GitCommand(self,
1716 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001717 capture_stdout=True,
1718 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001719
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001720 def PruneHeads(self):
1721 """Prune any topic branches already merged into upstream.
1722 """
1723 cb = self.CurrentBranch
1724 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001725 left = self._allrefs
1726 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727 if name.startswith(R_HEADS):
1728 name = name[len(R_HEADS):]
1729 if cb is None or name != cb:
1730 kill.append(name)
1731
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001732 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001733 if cb is not None \
1734 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001735 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001736 self.work_git.DetachHead(HEAD)
1737 kill.append(cb)
1738
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001739 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001740 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001741
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001742 try:
1743 self.bare_git.DetachHead(rev)
1744
1745 b = ['branch', '-d']
1746 b.extend(kill)
1747 b = GitCommand(self, b, bare=True,
1748 capture_stdout=True,
1749 capture_stderr=True)
1750 b.Wait()
1751 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001752 if ID_RE.match(old):
1753 self.bare_git.DetachHead(old)
1754 else:
1755 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001756 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001757
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001758 for branch in kill:
1759 if (R_HEADS + branch) not in left:
1760 self.CleanPublishedCache()
1761 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001762
1763 if cb and cb not in kill:
1764 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001765 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001766
1767 kept = []
1768 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001769 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001770 branch = self.GetBranch(branch)
1771 base = branch.LocalMerge
1772 if not base:
1773 base = rev
1774 kept.append(ReviewableBranch(self, branch, base))
1775 return kept
1776
1777
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001778# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001779
1780 def GetRegisteredSubprojects(self):
1781 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001782
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001783 def rec(subprojects):
1784 if not subprojects:
1785 return
1786 result.extend(subprojects)
1787 for p in subprojects:
1788 rec(p.subprojects)
1789 rec(self.subprojects)
1790 return result
1791
1792 def _GetSubmodules(self):
1793 # Unfortunately we cannot call `git submodule status --recursive` here
1794 # because the working tree might not exist yet, and it cannot be used
1795 # without a working tree in its current implementation.
1796
1797 def get_submodules(gitdir, rev):
1798 # Parse .gitmodules for submodule sub_paths and sub_urls
1799 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1800 if not sub_paths:
1801 return []
1802 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1803 # revision of submodule repository
1804 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1805 submodules = []
1806 for sub_path, sub_url in zip(sub_paths, sub_urls):
1807 try:
1808 sub_rev = sub_revs[sub_path]
1809 except KeyError:
1810 # Ignore non-exist submodules
1811 continue
1812 submodules.append((sub_rev, sub_path, sub_url))
1813 return submodules
1814
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001815 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1816 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001817
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001818 def parse_gitmodules(gitdir, rev):
1819 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1820 try:
Anthony King7bdac712014-07-16 12:56:40 +01001821 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1822 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001823 except GitError:
1824 return [], []
1825 if p.Wait() != 0:
1826 return [], []
1827
1828 gitmodules_lines = []
1829 fd, temp_gitmodules_path = tempfile.mkstemp()
1830 try:
1831 os.write(fd, p.stdout)
1832 os.close(fd)
1833 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001834 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1835 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001836 if p.Wait() != 0:
1837 return [], []
1838 gitmodules_lines = p.stdout.split('\n')
1839 except GitError:
1840 return [], []
1841 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001842 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001843
1844 names = set()
1845 paths = {}
1846 urls = {}
1847 for line in gitmodules_lines:
1848 if not line:
1849 continue
1850 m = re_path.match(line)
1851 if m:
1852 names.add(m.group(1))
1853 paths[m.group(1)] = m.group(2)
1854 continue
1855 m = re_url.match(line)
1856 if m:
1857 names.add(m.group(1))
1858 urls[m.group(1)] = m.group(2)
1859 continue
1860 names = sorted(names)
1861 return ([paths.get(name, '') for name in names],
1862 [urls.get(name, '') for name in names])
1863
1864 def git_ls_tree(gitdir, rev, paths):
1865 cmd = ['ls-tree', rev, '--']
1866 cmd.extend(paths)
1867 try:
Anthony King7bdac712014-07-16 12:56:40 +01001868 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1869 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001870 except GitError:
1871 return []
1872 if p.Wait() != 0:
1873 return []
1874 objects = {}
1875 for line in p.stdout.split('\n'):
1876 if not line.strip():
1877 continue
1878 object_rev, object_path = line.split()[2:4]
1879 objects[object_path] = object_rev
1880 return objects
1881
1882 try:
1883 rev = self.GetRevisionId()
1884 except GitError:
1885 return []
1886 return get_submodules(self.gitdir, rev)
1887
1888 def GetDerivedSubprojects(self):
1889 result = []
1890 if not self.Exists:
1891 # If git repo does not exist yet, querying its submodules will
1892 # mess up its states; so return here.
1893 return result
1894 for rev, path, url in self._GetSubmodules():
1895 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001896 relpath, worktree, gitdir, objdir = \
1897 self.manifest.GetSubprojectPaths(self, name, path)
1898 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001899 if project:
1900 result.extend(project.GetDerivedSubprojects())
1901 continue
David James8d201162013-10-11 17:03:19 -07001902
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001903 if url.startswith('..'):
1904 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001905 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001906 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001907 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001908 review=self.remote.review,
1909 revision=self.remote.revision)
1910 subproject = Project(manifest=self.manifest,
1911 name=name,
1912 remote=remote,
1913 gitdir=gitdir,
1914 objdir=objdir,
1915 worktree=worktree,
1916 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001917 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001918 revisionId=rev,
1919 rebase=self.rebase,
1920 groups=self.groups,
1921 sync_c=self.sync_c,
1922 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001923 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001924 parent=self,
1925 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001926 result.append(subproject)
1927 result.extend(subproject.GetDerivedSubprojects())
1928 return result
1929
1930
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001931# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001932 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001933 try:
1934 # if revision (sha or tag) is not present then following function
1935 # throws an error.
1936 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1937 return True
1938 except GitError:
1939 # There is no such persistent revision. We have to fetch it.
1940 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001941
Julien Campergue335f5ef2013-10-16 11:02:35 +02001942 def _FetchArchive(self, tarpath, cwd=None):
1943 cmd = ['archive', '-v', '-o', tarpath]
1944 cmd.append('--remote=%s' % self.remote.url)
1945 cmd.append('--prefix=%s/' % self.relpath)
1946 cmd.append(self.revisionExpr)
1947
1948 command = GitCommand(self, cmd, cwd=cwd,
1949 capture_stdout=True,
1950 capture_stderr=True)
1951
1952 if command.Wait() != 0:
1953 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1954
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001955 def _RemoteFetch(self, name=None,
1956 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001957 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001958 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001959 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001960 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001961 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001962 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001963 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001964 force_sync=False,
1965 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001966
1967 is_sha1 = False
1968 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001969 # The depth should not be used when fetching to a mirror because
1970 # it will result in a shallow repository that cannot be cloned or
1971 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001972 # The repo project should also never be synced with partial depth.
1973 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1974 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001975
Shawn Pearce69e04d82014-01-29 12:48:54 -08001976 if depth:
1977 current_branch_only = True
1978
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001979 if ID_RE.match(self.revisionExpr) is not None:
1980 is_sha1 = True
1981
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001982 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001983 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001984 # this is a tag and its sha1 value should never change
1985 tag_name = self.revisionExpr[len(R_TAGS):]
1986
1987 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001988 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001989 if not quiet:
1990 print('Skipped fetching project %s (already have persistent ref)'
1991 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001992 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001993 if is_sha1 and not depth:
1994 # When syncing a specific commit and --depth is not set:
1995 # * if upstream is explicitly specified and is not a sha1, fetch only
1996 # upstream as users expect only upstream to be fetch.
1997 # Note: The commit might not be in upstream in which case the sync
1998 # will fail.
1999 # * otherwise, fetch all branches to make sure we end up with the
2000 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002001 if self.upstream:
2002 current_branch_only = not ID_RE.match(self.upstream)
2003 else:
2004 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002005
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002006 if not name:
2007 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002008
2009 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002010 remote = self.GetRemote(name)
2011 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002012 ssh_proxy = True
2013
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002015 if alt_dir and 'objects' == os.path.basename(alt_dir):
2016 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002017 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2018 remote = self.GetRemote(name)
2019
David Pursehouse8a68ff92012-09-24 12:15:13 +09002020 all_refs = self.bare_ref.all
2021 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002022 tmp = set()
2023
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302024 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002025 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002026 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002027 all_refs[r] = ref_id
2028 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002029 continue
2030
David Pursehouse8a68ff92012-09-24 12:15:13 +09002031 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002032 continue
2033
David Pursehouse8a68ff92012-09-24 12:15:13 +09002034 r = 'refs/_alt/%s' % ref_id
2035 all_refs[r] = ref_id
2036 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002037 tmp.add(r)
2038
heping3d7bbc92017-04-12 19:51:47 +08002039 tmp_packed_lines = []
2040 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002041
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302042 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002043 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002044 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002045 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002046 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002047
heping3d7bbc92017-04-12 19:51:47 +08002048 tmp_packed = ''.join(tmp_packed_lines)
2049 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002051 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002052 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002053
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002054 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002055
Xin Li745be2e2019-06-03 11:24:30 -07002056 if clone_filter:
2057 git_require((2, 19, 0), fail=True, msg='partial clones')
2058 cmd.append('--filter=%s' % clone_filter)
2059 self.config.SetString('extensions.partialclone', self.remote.name)
2060
Conley Owensf97e8382015-01-21 11:12:46 -08002061 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002062 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002063 else:
2064 # If this repo has shallow objects, then we don't know which refs have
2065 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2066 # do this with projects that don't have shallow objects, since it is less
2067 # efficient.
2068 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2069 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002070
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002071 if quiet:
2072 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002073 if not self.worktree:
2074 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002075 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002076
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002077 # If using depth then we should not get all the tags since they may
2078 # be outside of the depth.
2079 if no_tags or depth:
2080 cmd.append('--no-tags')
2081 else:
2082 cmd.append('--tags')
2083
Mike Frysingere57f1142019-03-18 21:27:54 -04002084 if force_sync:
2085 cmd.append('--force')
2086
David Pursehouse74cfd272015-10-14 10:50:15 +09002087 if prune:
2088 cmd.append('--prune')
2089
Martin Kellye4e94d22017-03-21 16:05:12 -07002090 if submodules:
2091 cmd.append('--recurse-submodules=on-demand')
2092
Conley Owens80b87fe2014-05-09 17:13:44 -07002093 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002094 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002095 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002096 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002097 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002098 spec.append('tag')
2099 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002100
David Pursehouse403b64e2015-04-27 10:41:33 +09002101 if not self.manifest.IsMirror:
2102 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002103 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002104 # Shallow checkout of a specific commit, fetch from that commit and not
2105 # the heads only as the commit might be deeper in the history.
2106 spec.append(branch)
2107 else:
2108 if is_sha1:
2109 branch = self.upstream
2110 if branch is not None and branch.strip():
2111 if not branch.startswith('refs/'):
2112 branch = R_HEADS + branch
2113 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002114 cmd.extend(spec)
2115
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002116 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002117 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002118 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002119 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002120 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002121 ok = True
2122 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002123 # If needed, run the 'git remote prune' the first time through the loop
2124 elif (not _i and
2125 "error:" in gitcmd.stderr and
2126 "git remote prune" in gitcmd.stderr):
2127 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002128 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002129 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002130 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002131 break
2132 continue
Brian Harring14a66742012-09-28 20:21:57 -07002133 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002134 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2135 # in sha1 mode, we just tried sync'ing from the upstream field; it
2136 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002137 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002138 elif ret < 0:
2139 # Git died with a signal, exit immediately
2140 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002141 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002142
2143 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002144 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002145 if old_packed != '':
2146 _lwrite(packed_refs, old_packed)
2147 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002148 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002149 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002150
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002151 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002152 # We just synced the upstream given branch; verify we
2153 # got what we wanted, else trigger a second run of all
2154 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002155 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002156 if current_branch_only and depth:
2157 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002158 return self._RemoteFetch(name=name,
2159 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002160 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002161 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002162 else:
2163 # Avoid infinite recursion: sync all branches with depth set to None
2164 return self._RemoteFetch(name=name, current_branch_only=False,
2165 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002166 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002167
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002168 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002169
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002170 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002171 if initial and \
2172 (self.manifest.manifestProject.config.GetString('repo.depth') or
2173 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002174 return False
2175
2176 remote = self.GetRemote(self.remote.name)
2177 bundle_url = remote.url + '/clone.bundle'
2178 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002179 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2180 'persistent-http',
2181 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002182 return False
2183
2184 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2185 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2186
2187 exist_dst = os.path.exists(bundle_dst)
2188 exist_tmp = os.path.exists(bundle_tmp)
2189
2190 if not initial and not exist_dst and not exist_tmp:
2191 return False
2192
2193 if not exist_dst:
2194 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2195 if not exist_dst:
2196 return False
2197
2198 cmd = ['fetch']
2199 if quiet:
2200 cmd.append('--quiet')
2201 if not self.worktree:
2202 cmd.append('--update-head-ok')
2203 cmd.append(bundle_dst)
2204 for f in remote.fetch:
2205 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002206 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002207
2208 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002209 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002210 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002211 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002212 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002213 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002214
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002215 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002216 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002217 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002218
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002219 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002220 if quiet:
2221 cmd += ['--silent']
2222 if os.path.exists(tmpPath):
2223 size = os.stat(tmpPath).st_size
2224 if size >= 1024:
2225 cmd += ['--continue-at', '%d' % (size,)]
2226 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002227 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002228 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002229 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002230 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002231 if proxy:
2232 cmd += ['--proxy', proxy]
2233 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2234 cmd += ['--proxy', os.environ['http_proxy']]
2235 if srcUrl.startswith('persistent-https'):
2236 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2237 elif srcUrl.startswith('persistent-http'):
2238 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002239 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002240
Dave Borowitz137d0132015-01-02 11:12:54 -08002241 if IsTrace():
2242 Trace('%s', ' '.join(cmd))
2243 try:
2244 proc = subprocess.Popen(cmd)
2245 except OSError:
2246 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002247
Dave Borowitz137d0132015-01-02 11:12:54 -08002248 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002249
Dave Borowitz137d0132015-01-02 11:12:54 -08002250 if curlret == 22:
2251 # From curl man page:
2252 # 22: HTTP page not retrieved. The requested url was not found or
2253 # returned another error with the HTTP error code being 400 or above.
2254 # This return code only appears if -f, --fail is used.
2255 if not quiet:
2256 print("Server does not provide clone.bundle; ignoring.",
2257 file=sys.stderr)
2258 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002259
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002260 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002261 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002262 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002263 return True
2264 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002265 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002266 return False
2267 else:
2268 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002269
Kris Giesingc8d882a2014-12-23 13:02:32 -08002270 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002271 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002272 with open(path, 'rb') as f:
2273 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002274 return True
2275 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002276 if not quiet:
2277 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002278 return False
2279 except OSError:
2280 return False
2281
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002282 def _Checkout(self, rev, quiet=False):
2283 cmd = ['checkout']
2284 if quiet:
2285 cmd.append('-q')
2286 cmd.append(rev)
2287 cmd.append('--')
2288 if GitCommand(self, cmd).Wait() != 0:
2289 if self._allrefs:
2290 raise GitError('%s checkout %s ' % (self.name, rev))
2291
Anthony King7bdac712014-07-16 12:56:40 +01002292 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002293 cmd = ['cherry-pick']
2294 cmd.append(rev)
2295 cmd.append('--')
2296 if GitCommand(self, cmd).Wait() != 0:
2297 if self._allrefs:
2298 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2299
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302300 def _LsRemote(self, refs):
2301 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302302 p = GitCommand(self, cmd, capture_stdout=True)
2303 if p.Wait() == 0:
2304 if hasattr(p.stdout, 'decode'):
2305 return p.stdout.decode('utf-8')
2306 else:
2307 return p.stdout
2308 return None
2309
Anthony King7bdac712014-07-16 12:56:40 +01002310 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002311 cmd = ['revert']
2312 cmd.append('--no-edit')
2313 cmd.append(rev)
2314 cmd.append('--')
2315 if GitCommand(self, cmd).Wait() != 0:
2316 if self._allrefs:
2317 raise GitError('%s revert %s ' % (self.name, rev))
2318
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002319 def _ResetHard(self, rev, quiet=True):
2320 cmd = ['reset', '--hard']
2321 if quiet:
2322 cmd.append('-q')
2323 cmd.append(rev)
2324 if GitCommand(self, cmd).Wait() != 0:
2325 raise GitError('%s reset --hard %s ' % (self.name, rev))
2326
Martin Kellye4e94d22017-03-21 16:05:12 -07002327 def _SyncSubmodules(self, quiet=True):
2328 cmd = ['submodule', 'update', '--init', '--recursive']
2329 if quiet:
2330 cmd.append('-q')
2331 if GitCommand(self, cmd).Wait() != 0:
2332 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2333
Anthony King7bdac712014-07-16 12:56:40 +01002334 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002335 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002336 if onto is not None:
2337 cmd.extend(['--onto', onto])
2338 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002339 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002340 raise GitError('%s rebase %s ' % (self.name, upstream))
2341
Pierre Tardy3d125942012-05-04 12:18:12 +02002342 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002343 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002344 if ffonly:
2345 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002346 if GitCommand(self, cmd).Wait() != 0:
2347 raise GitError('%s merge %s ' % (self.name, head))
2348
Kevin Degiabaa7f32014-11-12 11:27:45 -07002349 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002350 init_git_dir = not os.path.exists(self.gitdir)
2351 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002352 try:
2353 # Initialize the bare repository, which contains all of the objects.
2354 if init_obj_dir:
2355 os.makedirs(self.objdir)
2356 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002357
Kevin Degib1a07b82015-07-27 13:33:43 -06002358 # If we have a separate directory to hold refs, initialize it as well.
2359 if self.objdir != self.gitdir:
2360 if init_git_dir:
2361 os.makedirs(self.gitdir)
2362
2363 if init_obj_dir or init_git_dir:
2364 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2365 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002366 try:
2367 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2368 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002369 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002370 print("Retrying clone after deleting %s" %
2371 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002372 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002373 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2374 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002375 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002376 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002377 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2378 except:
2379 raise e
2380 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002381
Kevin Degi384b3c52014-10-16 16:02:58 -06002382 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002383 mp = self.manifest.manifestProject
2384 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002385
Kevin Degib1a07b82015-07-27 13:33:43 -06002386 if ref_dir or mirror_git:
2387 if not mirror_git:
2388 mirror_git = os.path.join(ref_dir, self.name + '.git')
2389 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2390 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002391
Kevin Degib1a07b82015-07-27 13:33:43 -06002392 if os.path.exists(mirror_git):
2393 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002394
Kevin Degib1a07b82015-07-27 13:33:43 -06002395 elif os.path.exists(repo_git):
2396 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002397
Kevin Degib1a07b82015-07-27 13:33:43 -06002398 else:
2399 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002400
Kevin Degib1a07b82015-07-27 13:33:43 -06002401 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002402 if not os.path.isabs(ref_dir):
2403 # The alternate directory is relative to the object database.
2404 ref_dir = os.path.relpath(ref_dir,
2405 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002406 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2407 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002408
Kevin Degib1a07b82015-07-27 13:33:43 -06002409 self._UpdateHooks()
2410
2411 m = self.manifest.manifestProject.config
2412 for key in ['user.name', 'user.email']:
2413 if m.Has(key, include_defaults=False):
2414 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002415 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002416 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002417 if self.manifest.IsMirror:
2418 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002419 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002420 self.config.SetString('core.bare', None)
2421 except Exception:
2422 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002423 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002424 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002425 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002426 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002427
Jimmie Westera0444582012-10-24 13:44:42 +02002428 def _UpdateHooks(self):
2429 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002430 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002431
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002432 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002433 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002434 if not os.path.exists(hooks):
2435 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002436 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002437 name = os.path.basename(stock_hook)
2438
Victor Boivie65e0f352011-04-18 11:23:29 +02002439 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002440 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002441 # Don't install a Gerrit Code Review hook if this
2442 # project does not appear to use it for reviews.
2443 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002444 # Since the manifest project is one of those, but also
2445 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002446 continue
2447
2448 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002449 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002450 continue
2451 if os.path.exists(dst):
2452 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002453 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002454 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002455 _warn("%s: Not replacing locally modified %s hook",
2456 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002457 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002458 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002459 platform_utils.symlink(
2460 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002461 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002462 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002463 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002464 else:
2465 raise
2466
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002467 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002468 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002469 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002470 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002471 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002472 remote.review = self.remote.review
2473 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002474
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002475 if self.worktree:
2476 remote.ResetFetch(mirror=False)
2477 else:
2478 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002479 remote.Save()
2480
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002481 def _InitMRef(self):
2482 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002483 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002484
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002485 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002486 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002487
2488 def _InitAnyMRef(self, ref):
2489 cur = self.bare_ref.symref(ref)
2490
2491 if self.revisionId:
2492 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2493 msg = 'manifest set to %s' % self.revisionId
2494 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002495 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002496 else:
2497 remote = self.GetRemote(self.remote.name)
2498 dst = remote.ToLocal(self.revisionExpr)
2499 if cur != dst:
2500 msg = 'manifest set to %s' % self.revisionExpr
2501 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002502
Kevin Degi384b3c52014-10-16 16:02:58 -06002503 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002504 symlink_files = self.shareable_files[:]
2505 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002506 if share_refs:
2507 symlink_files += self.working_tree_files
2508 symlink_dirs += self.working_tree_dirs
2509 to_symlink = symlink_files + symlink_dirs
2510 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002511 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002512 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002513 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002514 # Fail if the links are pointing to the wrong place
2515 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002516 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002517 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002518 'work tree. If you\'re comfortable with the '
2519 'possibility of losing the work tree\'s git metadata,'
2520 ' use `repo sync --force-sync {0}` to '
2521 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002522
David James8d201162013-10-11 17:03:19 -07002523 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2524 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2525
2526 Args:
2527 gitdir: The bare git repository. Must already be initialized.
2528 dotgit: The repository you would like to initialize.
2529 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2530 Only one work tree can store refs under a given |gitdir|.
2531 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2532 This saves you the effort of initializing |dotgit| yourself.
2533 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002534 symlink_files = self.shareable_files[:]
2535 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002536 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002537 symlink_files += self.working_tree_files
2538 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002539 to_symlink = symlink_files + symlink_dirs
2540
2541 to_copy = []
2542 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002543 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002544
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002545 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002546 for name in set(to_copy).union(to_symlink):
2547 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002548 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002549 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002550
Kevin Degi384b3c52014-10-16 16:02:58 -06002551 if os.path.lexists(dst):
2552 continue
David James8d201162013-10-11 17:03:19 -07002553
2554 # If the source dir doesn't exist, create an empty dir.
2555 if name in symlink_dirs and not os.path.lexists(src):
2556 os.makedirs(src)
2557
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002558 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002559 platform_utils.symlink(
2560 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002561 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002562 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002563 shutil.copytree(src, dst)
2564 elif os.path.isfile(src):
2565 shutil.copy(src, dst)
2566
Conley Owens80b87fe2014-05-09 17:13:44 -07002567 # If the source file doesn't exist, ensure the destination
2568 # file doesn't either.
2569 if name in symlink_files and not os.path.lexists(src):
2570 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002571 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002572 except OSError:
2573 pass
2574
David James8d201162013-10-11 17:03:19 -07002575 except OSError as e:
2576 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002577 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002578 else:
2579 raise
2580
Martin Kellye4e94d22017-03-21 16:05:12 -07002581 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002582 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002583 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002584 try:
2585 if init_dotgit:
2586 os.makedirs(dotgit)
2587 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2588 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002589
Kevin Degiabaa7f32014-11-12 11:27:45 -07002590 try:
2591 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2592 except GitError as e:
2593 if force_sync:
2594 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002595 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002596 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002597 except:
2598 raise e
2599 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002600
Kevin Degib1a07b82015-07-27 13:33:43 -06002601 if init_dotgit:
2602 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002603
Kevin Degib1a07b82015-07-27 13:33:43 -06002604 cmd = ['read-tree', '--reset', '-u']
2605 cmd.append('-v')
2606 cmd.append(HEAD)
2607 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002608 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002609
Martin Kellye4e94d22017-03-21 16:05:12 -07002610 if submodules:
2611 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002612 self._CopyAndLinkFiles()
2613 except Exception:
2614 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002615 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002616 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002617
Renaud Paquay788e9622017-01-27 11:41:12 -08002618 def _get_symlink_error_message(self):
2619 if platform_utils.isWindows():
2620 return ('Unable to create symbolic link. Please re-run the command as '
2621 'Administrator, or see '
2622 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2623 'for other options.')
2624 return 'filesystem must support symlinks'
2625
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002626 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002627 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002628
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002629 def _revlist(self, *args, **kw):
2630 a = []
2631 a.extend(args)
2632 a.append('--')
2633 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002634
2635 @property
2636 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002637 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002638
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002639 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002640 """Get logs between two revisions of this project."""
2641 comp = '..'
2642 if rev1:
2643 revs = [rev1]
2644 if rev2:
2645 revs.extend([comp, rev2])
2646 cmd = ['log', ''.join(revs)]
2647 out = DiffColoring(self.config)
2648 if out.is_on and color:
2649 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002650 if pretty_format is not None:
2651 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002652 if oneline:
2653 cmd.append('--oneline')
2654
2655 try:
2656 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2657 if log.Wait() == 0:
2658 return log.stdout
2659 except GitError:
2660 # worktree may not exist if groups changed for example. In that case,
2661 # try in gitdir instead.
2662 if not os.path.exists(self.worktree):
2663 return self.bare_git.log(*cmd[1:])
2664 else:
2665 raise
2666 return None
2667
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002668 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2669 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002670 """Get the list of logs from this revision to given revisionId"""
2671 logs = {}
2672 selfId = self.GetRevisionId(self._allrefs)
2673 toId = toProject.GetRevisionId(toProject._allrefs)
2674
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002675 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2676 pretty_format=pretty_format)
2677 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2678 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002679 return logs
2680
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002681 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002682
David James8d201162013-10-11 17:03:19 -07002683 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002684 self._project = project
2685 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002686 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002687
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002688 def LsOthers(self):
2689 p = GitCommand(self._project,
2690 ['ls-files',
2691 '-z',
2692 '--others',
2693 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002694 bare=False,
David James8d201162013-10-11 17:03:19 -07002695 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002696 capture_stdout=True,
2697 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002698 if p.Wait() == 0:
2699 out = p.stdout
2700 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002701 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002702 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002703 return []
2704
2705 def DiffZ(self, name, *args):
2706 cmd = [name]
2707 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002708 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709 cmd.extend(args)
2710 p = GitCommand(self._project,
2711 cmd,
David James8d201162013-10-11 17:03:19 -07002712 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002713 bare=False,
2714 capture_stdout=True,
2715 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002716 try:
2717 out = p.process.stdout.read()
2718 r = {}
2719 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002720 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002721 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002722 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002723 info = next(out)
2724 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002725 except StopIteration:
2726 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002727
2728 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002729
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002730 def __init__(self, path, omode, nmode, oid, nid, state):
2731 self.path = path
2732 self.src_path = None
2733 self.old_mode = omode
2734 self.new_mode = nmode
2735 self.old_id = oid
2736 self.new_id = nid
2737
2738 if len(state) == 1:
2739 self.status = state
2740 self.level = None
2741 else:
2742 self.status = state[:1]
2743 self.level = state[1:]
2744 while self.level.startswith('0'):
2745 self.level = self.level[1:]
2746
2747 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002748 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002749 if info.status in ('R', 'C'):
2750 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002751 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002752 r[info.path] = info
2753 return r
2754 finally:
2755 p.Wait()
2756
2757 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002758 if self._bare:
2759 path = os.path.join(self._project.gitdir, HEAD)
2760 else:
2761 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002762 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002763 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002764 except IOError as e:
2765 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002766 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002767 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002768 finally:
2769 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302770 try:
2771 line = line.decode()
2772 except AttributeError:
2773 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002774 if line.startswith('ref: '):
2775 return line[5:-1]
2776 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002777
2778 def SetHead(self, ref, message=None):
2779 cmdv = []
2780 if message is not None:
2781 cmdv.extend(['-m', message])
2782 cmdv.append(HEAD)
2783 cmdv.append(ref)
2784 self.symbolic_ref(*cmdv)
2785
2786 def DetachHead(self, new, message=None):
2787 cmdv = ['--no-deref']
2788 if message is not None:
2789 cmdv.extend(['-m', message])
2790 cmdv.append(HEAD)
2791 cmdv.append(new)
2792 self.update_ref(*cmdv)
2793
2794 def UpdateRef(self, name, new, old=None,
2795 message=None,
2796 detach=False):
2797 cmdv = []
2798 if message is not None:
2799 cmdv.extend(['-m', message])
2800 if detach:
2801 cmdv.append('--no-deref')
2802 cmdv.append(name)
2803 cmdv.append(new)
2804 if old is not None:
2805 cmdv.append(old)
2806 self.update_ref(*cmdv)
2807
2808 def DeleteRef(self, name, old=None):
2809 if not old:
2810 old = self.rev_parse(name)
2811 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002812 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002813
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002814 def rev_list(self, *args, **kw):
2815 if 'format' in kw:
2816 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2817 else:
2818 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002819 cmdv.extend(args)
2820 p = GitCommand(self._project,
2821 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002822 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002823 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002824 capture_stdout=True,
2825 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002826 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002827 raise GitError('%s rev-list %s: %s' %
2828 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002829 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002830
2831 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002832 """Allow arbitrary git commands using pythonic syntax.
2833
2834 This allows you to do things like:
2835 git_obj.rev_parse('HEAD')
2836
2837 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2838 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002839 Any other positional arguments will be passed to the git command, and the
2840 following keyword arguments are supported:
2841 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002842
2843 Args:
2844 name: The name of the git command to call. Any '_' characters will
2845 be replaced with '-'.
2846
2847 Returns:
2848 A callable object that will try to call git with the named command.
2849 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002850 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002851
Dave Borowitz091f8932012-10-23 17:01:04 -07002852 def runner(*args, **kwargs):
2853 cmdv = []
2854 config = kwargs.pop('config', None)
2855 for k in kwargs:
2856 raise TypeError('%s() got an unexpected keyword argument %r'
2857 % (name, k))
2858 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002859 if not git_require((1, 7, 2)):
2860 raise ValueError('cannot set config on command line for %s()'
2861 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302862 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002863 cmdv.append('-c')
2864 cmdv.append('%s=%s' % (k, v))
2865 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002866 cmdv.extend(args)
2867 p = GitCommand(self._project,
2868 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002869 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002870 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002871 capture_stdout=True,
2872 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002873 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002874 raise GitError('%s %s: %s' %
2875 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002876 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302877 try:
Conley Owensedd01512013-09-26 12:59:58 -07002878 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302879 except AttributeError:
2880 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002881 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2882 return r[:-1]
2883 return r
2884 return runner
2885
2886
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002887class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002888
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002889 def __str__(self):
2890 return 'prior sync failed; rebase still in progress'
2891
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002892
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002893class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002894
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002895 def __str__(self):
2896 return 'contains uncommitted changes'
2897
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002898
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002899class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002900
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002901 def __init__(self, project, text):
2902 self.project = project
2903 self.text = text
2904
2905 def Print(self, syncbuf):
2906 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2907 syncbuf.out.nl()
2908
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002909
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002910class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002911
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002912 def __init__(self, project, why):
2913 self.project = project
2914 self.why = why
2915
2916 def Print(self, syncbuf):
2917 syncbuf.out.fail('error: %s/: %s',
2918 self.project.relpath,
2919 str(self.why))
2920 syncbuf.out.nl()
2921
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002922
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002923class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002924
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002925 def __init__(self, project, action):
2926 self.project = project
2927 self.action = action
2928
2929 def Run(self, syncbuf):
2930 out = syncbuf.out
2931 out.project('project %s/', self.project.relpath)
2932 out.nl()
2933 try:
2934 self.action()
2935 out.nl()
2936 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002937 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002938 out.nl()
2939 return False
2940
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002941
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002942class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002943
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002944 def __init__(self, config):
2945 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002946 self.project = self.printer('header', attr='bold')
2947 self.info = self.printer('info')
2948 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002949
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002950
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002951class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002952
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002953 def __init__(self, config, detach_head=False):
2954 self._messages = []
2955 self._failures = []
2956 self._later_queue1 = []
2957 self._later_queue2 = []
2958
2959 self.out = _SyncColoring(config)
2960 self.out.redirect(sys.stderr)
2961
2962 self.detach_head = detach_head
2963 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002964 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002965
2966 def info(self, project, fmt, *args):
2967 self._messages.append(_InfoMessage(project, fmt % args))
2968
2969 def fail(self, project, err=None):
2970 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002971 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002972
2973 def later1(self, project, what):
2974 self._later_queue1.append(_Later(project, what))
2975
2976 def later2(self, project, what):
2977 self._later_queue2.append(_Later(project, what))
2978
2979 def Finish(self):
2980 self._PrintMessages()
2981 self._RunLater()
2982 self._PrintMessages()
2983 return self.clean
2984
David Rileye0684ad2017-04-05 00:02:59 -07002985 def Recently(self):
2986 recent_clean = self.recent_clean
2987 self.recent_clean = True
2988 return recent_clean
2989
2990 def _MarkUnclean(self):
2991 self.clean = False
2992 self.recent_clean = False
2993
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002994 def _RunLater(self):
2995 for q in ['_later_queue1', '_later_queue2']:
2996 if not self._RunQueue(q):
2997 return
2998
2999 def _RunQueue(self, queue):
3000 for m in getattr(self, queue):
3001 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003002 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003003 return False
3004 setattr(self, queue, [])
3005 return True
3006
3007 def _PrintMessages(self):
3008 for m in self._messages:
3009 m.Print(self)
3010 for m in self._failures:
3011 m.Print(self)
3012
3013 self._messages = []
3014 self._failures = []
3015
3016
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003017class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003018
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003019 """A special project housed under .repo.
3020 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003021
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003022 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003023 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003024 manifest=manifest,
3025 name=name,
3026 gitdir=gitdir,
3027 objdir=gitdir,
3028 worktree=worktree,
3029 remote=RemoteSpec('origin'),
3030 relpath='.repo/%s' % name,
3031 revisionExpr='refs/heads/master',
3032 revisionId=None,
3033 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003034
3035 def PreSync(self):
3036 if self.Exists:
3037 cb = self.CurrentBranch
3038 if cb:
3039 base = self.GetBranch(cb).merge
3040 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003041 self.revisionExpr = base
3042 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003043
Martin Kelly224a31a2017-07-10 14:46:25 -07003044 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003045 """ Prepare MetaProject for manifest branch switch
3046 """
3047
3048 # detach and delete manifest branch, allowing a new
3049 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003050 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003051 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003052 syncbuf.Finish()
3053
3054 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003055 ['update-ref', '-d', 'refs/heads/default'],
3056 capture_stdout=True,
3057 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003058
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003059 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003060 def LastFetch(self):
3061 try:
3062 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3063 return os.path.getmtime(fh)
3064 except OSError:
3065 return 0
3066
3067 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003068 def HasChanges(self):
3069 """Has the remote received new commits not yet checked out?
3070 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003071 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003072 return False
3073
David Pursehouse8a68ff92012-09-24 12:15:13 +09003074 all_refs = self.bare_ref.all
3075 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003076 head = self.work_git.GetHead()
3077 if head.startswith(R_HEADS):
3078 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003079 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003080 except KeyError:
3081 head = None
3082
3083 if revid == head:
3084 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003085 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003086 return True
3087 return False