blob: 8a38c5849c781aaa290c0778338d4957f509f540 [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,
1229 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 """Perform only the network IO portion of the sync process.
1231 Local working directory/branch state is not affected.
1232 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001233 if archive and not isinstance(self, MetaProject):
1234 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001235 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001236 return False
1237
1238 name = self.relpath.replace('\\', '/')
1239 name = name.replace('/', '_')
1240 tarpath = '%s.tar' % name
1241 topdir = self.manifest.topdir
1242
1243 try:
1244 self._FetchArchive(tarpath, cwd=topdir)
1245 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001246 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001247 return False
1248
1249 # From now on, we only need absolute tarpath
1250 tarpath = os.path.join(topdir, tarpath)
1251
1252 if not self._ExtractArchive(tarpath, path=topdir):
1253 return False
1254 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001255 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001256 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001257 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001258 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001259 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001260 if is_new is None:
1261 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001262 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001263 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001264 else:
1265 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001267
1268 if is_new:
1269 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1270 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001271 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001272 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001273 # This works for both absolute and relative alternate directories.
1274 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001275 finally:
1276 fd.close()
1277 except IOError:
1278 alt_dir = None
1279 else:
1280 alt_dir = None
1281
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001282 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001283 and alt_dir is None \
1284 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001285 is_new = False
1286
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001287 if not current_branch_only:
1288 if self.sync_c:
1289 current_branch_only = True
1290 elif not self.manifest._loaded:
1291 # Manifest cannot check defaults until it syncs.
1292 current_branch_only = False
1293 elif self.manifest.default.sync_c:
1294 current_branch_only = True
1295
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001296 if not no_tags:
1297 if not self.sync_tags:
1298 no_tags = True
1299
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001300 if self.clone_depth:
1301 depth = self.clone_depth
1302 else:
1303 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1304
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001305 need_to_fetch = not (optimized_fetch and
1306 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001307 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001308 if (need_to_fetch and
1309 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1310 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001311 no_tags=no_tags, prune=prune, depth=depth,
Mike Frysingere57f1142019-03-18 21:27:54 -04001312 submodules=submodules, force_sync=force_sync)):
Anthony King7bdac712014-07-16 12:56:40 +01001313 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001314
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001315 mp = self.manifest.manifestProject
1316 dissociate = mp.config.GetBoolean('repo.dissociate')
1317 if dissociate:
1318 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1319 if os.path.exists(alternates_file):
1320 cmd = ['repack', '-a', '-d']
1321 if GitCommand(self, cmd, bare=True).Wait() != 0:
1322 return False
1323 platform_utils.remove(alternates_file)
1324
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001325 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001326 self._InitMRef()
1327 else:
1328 self._InitMirrorHead()
1329 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001330 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001331 except OSError:
1332 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001333 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001334
1335 def PostRepoUpgrade(self):
1336 self._InitHooks()
1337
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001338 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001339 if self.manifest.isGitcClient:
1340 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001341 for copyfile in self.copyfiles:
1342 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001343 for linkfile in self.linkfiles:
1344 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001345
Julien Camperguedd654222014-01-09 16:21:37 +01001346 def GetCommitRevisionId(self):
1347 """Get revisionId of a commit.
1348
1349 Use this method instead of GetRevisionId to get the id of the commit rather
1350 than the id of the current git object (for example, a tag)
1351
1352 """
1353 if not self.revisionExpr.startswith(R_TAGS):
1354 return self.GetRevisionId(self._allrefs)
1355
1356 try:
1357 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1358 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001359 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1360 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001361
David Pursehouse8a68ff92012-09-24 12:15:13 +09001362 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001363 if self.revisionId:
1364 return self.revisionId
1365
1366 rem = self.GetRemote(self.remote.name)
1367 rev = rem.ToLocal(self.revisionExpr)
1368
David Pursehouse8a68ff92012-09-24 12:15:13 +09001369 if all_refs is not None and rev in all_refs:
1370 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001371
1372 try:
1373 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1374 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001375 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1376 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001377
Martin Kellye4e94d22017-03-21 16:05:12 -07001378 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001379 """Perform only the local IO portion of the sync process.
1380 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001381 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001382 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001383 all_refs = self.bare_ref.all
1384 self.CleanPublishedCache(all_refs)
1385 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001386
David Pursehouse1d947b32012-10-25 12:23:11 +09001387 def _doff():
1388 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001389 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001390
Martin Kellye4e94d22017-03-21 16:05:12 -07001391 def _dosubmodules():
1392 self._SyncSubmodules(quiet=True)
1393
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001394 head = self.work_git.GetHead()
1395 if head.startswith(R_HEADS):
1396 branch = head[len(R_HEADS):]
1397 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001398 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001399 except KeyError:
1400 head = None
1401 else:
1402 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001403
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001404 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405 # Currently on a detached HEAD. The user is assumed to
1406 # not have any local modifications worth worrying about.
1407 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001408 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001409 syncbuf.fail(self, _PriorSyncFailedError())
1410 return
1411
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001412 if head == revid:
1413 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001414 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001415 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001416 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001417 # The copy/linkfile config may have changed.
1418 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001419 return
1420 else:
1421 lost = self._revlist(not_rev(revid), HEAD)
1422 if lost:
1423 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001424
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001425 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001426 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001427 if submodules:
1428 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001429 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001430 syncbuf.fail(self, e)
1431 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001432 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001433 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001435 if head == revid:
1436 # No changes; don't do anything further.
1437 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001438 # The copy/linkfile config may have changed.
1439 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001440 return
1441
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001442 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001444 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001446 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001447 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001448 syncbuf.info(self,
1449 "leaving %s; does not track upstream",
1450 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001451 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001452 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001453 if submodules:
1454 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001455 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001456 syncbuf.fail(self, e)
1457 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001458 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001459 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001460
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001461 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001462 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001463 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001464 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465 if not_merged:
1466 if upstream_gain:
1467 # The user has published this branch and some of those
1468 # commits are not yet merged upstream. We do not want
1469 # to rewrite the published commits so we punt.
1470 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001471 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001472 "branch %s is published (but not merged) and is now "
1473 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001474 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001475 elif pub == head:
1476 # All published commits are merged, and thus we are a
1477 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001478 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001479 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001480 if submodules:
1481 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001482 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001483
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001484 # Examine the local commits not in the remote. Find the
1485 # last one attributed to this user, if any.
1486 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001487 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001488 last_mine = None
1489 cnt_mine = 0
1490 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301491 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001492 if committer_email == self.UserEmail:
1493 last_mine = commit_id
1494 cnt_mine += 1
1495
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001496 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001497 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001498
1499 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001500 syncbuf.fail(self, _DirtyError())
1501 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001503 # If the upstream switched on us, warn the user.
1504 #
1505 if branch.merge != self.revisionExpr:
1506 if branch.merge and self.revisionExpr:
1507 syncbuf.info(self,
1508 'manifest switched %s...%s',
1509 branch.merge,
1510 self.revisionExpr)
1511 elif branch.merge:
1512 syncbuf.info(self,
1513 'manifest no longer tracks %s',
1514 branch.merge)
1515
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001516 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001517 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001518 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001520 syncbuf.info(self,
1521 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001522 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001524 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001525 if not ID_RE.match(self.revisionExpr):
1526 # in case of manifest sync the revisionExpr might be a SHA1
1527 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001528 if not branch.merge.startswith('refs/'):
1529 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001530 branch.Save()
1531
Mike Pontillod3153822012-02-28 11:53:24 -08001532 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001533 def _docopyandlink():
1534 self._CopyAndLinkFiles()
1535
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001536 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001537 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001538 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001539 if submodules:
1540 syncbuf.later2(self, _dosubmodules)
1541 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001542 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001543 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001544 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001545 if submodules:
1546 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001547 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001548 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001549 syncbuf.fail(self, e)
1550 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001551 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001552 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001553 if submodules:
1554 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001556 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557 # dest should already be an absolute path, but src is project relative
1558 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001559 abssrc = os.path.join(self.worktree, src)
1560 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001561
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001562 def AddLinkFile(self, src, dest, absdest):
1563 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001564 # make src relative path to dest
1565 absdestdir = os.path.dirname(absdest)
1566 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001567 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001568
James W. Mills24c13082012-04-12 15:04:13 -05001569 def AddAnnotation(self, name, value, keep):
1570 self.annotations.append(_Annotation(name, value, keep))
1571
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001572 def DownloadPatchSet(self, change_id, patch_id):
1573 """Download a single patch set of a single change to FETCH_HEAD.
1574 """
1575 remote = self.GetRemote(self.remote.name)
1576
1577 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001578 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001579 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001580 if GitCommand(self, cmd, bare=True).Wait() != 0:
1581 return None
1582 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001583 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001584 change_id,
1585 patch_id,
1586 self.bare_git.rev_parse('FETCH_HEAD'))
1587
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001588
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001589# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590
Simran Basib9a1b732015-08-20 12:19:28 -07001591 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001592 """Create a new branch off the manifest's revision.
1593 """
Simran Basib9a1b732015-08-20 12:19:28 -07001594 if not branch_merge:
1595 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001596 head = self.work_git.GetHead()
1597 if head == (R_HEADS + name):
1598 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001599
David Pursehouse8a68ff92012-09-24 12:15:13 +09001600 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001601 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001602 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001603 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001604 capture_stdout=True,
1605 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001606
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001607 branch = self.GetBranch(name)
1608 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001609 branch.merge = branch_merge
1610 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1611 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001612 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001613
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001614 if head.startswith(R_HEADS):
1615 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001616 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001617 except KeyError:
1618 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001619 if revid and head and revid == head:
1620 ref = os.path.join(self.gitdir, R_HEADS + name)
1621 try:
1622 os.makedirs(os.path.dirname(ref))
1623 except OSError:
1624 pass
1625 _lwrite(ref, '%s\n' % revid)
1626 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1627 'ref: %s%s\n' % (R_HEADS, name))
1628 branch.Save()
1629 return True
1630
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001631 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001632 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001633 capture_stdout=True,
1634 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001635 branch.Save()
1636 return True
1637 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638
Wink Saville02d79452009-04-10 13:01:24 -07001639 def CheckoutBranch(self, name):
1640 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001641
1642 Args:
1643 name: The name of the branch to checkout.
1644
1645 Returns:
1646 True if the checkout succeeded; False if it didn't; None if the branch
1647 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001648 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001649 rev = R_HEADS + name
1650 head = self.work_git.GetHead()
1651 if head == rev:
1652 # Already on the branch
1653 #
1654 return True
Wink Saville02d79452009-04-10 13:01:24 -07001655
David Pursehouse8a68ff92012-09-24 12:15:13 +09001656 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001657 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001658 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001659 except KeyError:
1660 # Branch does not exist in this project
1661 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001662 return None
Wink Saville02d79452009-04-10 13:01:24 -07001663
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001664 if head.startswith(R_HEADS):
1665 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001667 except KeyError:
1668 head = None
1669
1670 if head == revid:
1671 # Same revision; just update HEAD to point to the new
1672 # target branch, but otherwise take no other action.
1673 #
1674 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1675 'ref: %s%s\n' % (R_HEADS, name))
1676 return True
1677
1678 return GitCommand(self,
1679 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001680 capture_stdout=True,
1681 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001682
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001683 def AbandonBranch(self, name):
1684 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001685
1686 Args:
1687 name: The name of the branch to abandon.
1688
1689 Returns:
1690 True if the abandon succeeded; False if it didn't; None if the branch
1691 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001692 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001693 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001694 all_refs = self.bare_ref.all
1695 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001696 # Doesn't exist
1697 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001698
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001699 head = self.work_git.GetHead()
1700 if head == rev:
1701 # We can't destroy the branch while we are sitting
1702 # on it. Switch to a detached HEAD.
1703 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001704 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001705
David Pursehouse8a68ff92012-09-24 12:15:13 +09001706 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001707 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001708 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1709 '%s\n' % revid)
1710 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001711 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001712
1713 return GitCommand(self,
1714 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001715 capture_stdout=True,
1716 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001717
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001718 def PruneHeads(self):
1719 """Prune any topic branches already merged into upstream.
1720 """
1721 cb = self.CurrentBranch
1722 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001723 left = self._allrefs
1724 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001725 if name.startswith(R_HEADS):
1726 name = name[len(R_HEADS):]
1727 if cb is None or name != cb:
1728 kill.append(name)
1729
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001730 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001731 if cb is not None \
1732 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001733 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001734 self.work_git.DetachHead(HEAD)
1735 kill.append(cb)
1736
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001737 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001738 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001739
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001740 try:
1741 self.bare_git.DetachHead(rev)
1742
1743 b = ['branch', '-d']
1744 b.extend(kill)
1745 b = GitCommand(self, b, bare=True,
1746 capture_stdout=True,
1747 capture_stderr=True)
1748 b.Wait()
1749 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001750 if ID_RE.match(old):
1751 self.bare_git.DetachHead(old)
1752 else:
1753 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001754 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001755
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001756 for branch in kill:
1757 if (R_HEADS + branch) not in left:
1758 self.CleanPublishedCache()
1759 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001760
1761 if cb and cb not in kill:
1762 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001763 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001764
1765 kept = []
1766 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001767 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001768 branch = self.GetBranch(branch)
1769 base = branch.LocalMerge
1770 if not base:
1771 base = rev
1772 kept.append(ReviewableBranch(self, branch, base))
1773 return kept
1774
1775
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001776# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001777
1778 def GetRegisteredSubprojects(self):
1779 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001780
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001781 def rec(subprojects):
1782 if not subprojects:
1783 return
1784 result.extend(subprojects)
1785 for p in subprojects:
1786 rec(p.subprojects)
1787 rec(self.subprojects)
1788 return result
1789
1790 def _GetSubmodules(self):
1791 # Unfortunately we cannot call `git submodule status --recursive` here
1792 # because the working tree might not exist yet, and it cannot be used
1793 # without a working tree in its current implementation.
1794
1795 def get_submodules(gitdir, rev):
1796 # Parse .gitmodules for submodule sub_paths and sub_urls
1797 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1798 if not sub_paths:
1799 return []
1800 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1801 # revision of submodule repository
1802 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1803 submodules = []
1804 for sub_path, sub_url in zip(sub_paths, sub_urls):
1805 try:
1806 sub_rev = sub_revs[sub_path]
1807 except KeyError:
1808 # Ignore non-exist submodules
1809 continue
1810 submodules.append((sub_rev, sub_path, sub_url))
1811 return submodules
1812
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001813 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1814 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001815
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001816 def parse_gitmodules(gitdir, rev):
1817 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1818 try:
Anthony King7bdac712014-07-16 12:56:40 +01001819 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1820 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001821 except GitError:
1822 return [], []
1823 if p.Wait() != 0:
1824 return [], []
1825
1826 gitmodules_lines = []
1827 fd, temp_gitmodules_path = tempfile.mkstemp()
1828 try:
1829 os.write(fd, p.stdout)
1830 os.close(fd)
1831 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001832 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1833 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001834 if p.Wait() != 0:
1835 return [], []
1836 gitmodules_lines = p.stdout.split('\n')
1837 except GitError:
1838 return [], []
1839 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001840 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001841
1842 names = set()
1843 paths = {}
1844 urls = {}
1845 for line in gitmodules_lines:
1846 if not line:
1847 continue
1848 m = re_path.match(line)
1849 if m:
1850 names.add(m.group(1))
1851 paths[m.group(1)] = m.group(2)
1852 continue
1853 m = re_url.match(line)
1854 if m:
1855 names.add(m.group(1))
1856 urls[m.group(1)] = m.group(2)
1857 continue
1858 names = sorted(names)
1859 return ([paths.get(name, '') for name in names],
1860 [urls.get(name, '') for name in names])
1861
1862 def git_ls_tree(gitdir, rev, paths):
1863 cmd = ['ls-tree', rev, '--']
1864 cmd.extend(paths)
1865 try:
Anthony King7bdac712014-07-16 12:56:40 +01001866 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1867 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001868 except GitError:
1869 return []
1870 if p.Wait() != 0:
1871 return []
1872 objects = {}
1873 for line in p.stdout.split('\n'):
1874 if not line.strip():
1875 continue
1876 object_rev, object_path = line.split()[2:4]
1877 objects[object_path] = object_rev
1878 return objects
1879
1880 try:
1881 rev = self.GetRevisionId()
1882 except GitError:
1883 return []
1884 return get_submodules(self.gitdir, rev)
1885
1886 def GetDerivedSubprojects(self):
1887 result = []
1888 if not self.Exists:
1889 # If git repo does not exist yet, querying its submodules will
1890 # mess up its states; so return here.
1891 return result
1892 for rev, path, url in self._GetSubmodules():
1893 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001894 relpath, worktree, gitdir, objdir = \
1895 self.manifest.GetSubprojectPaths(self, name, path)
1896 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001897 if project:
1898 result.extend(project.GetDerivedSubprojects())
1899 continue
David James8d201162013-10-11 17:03:19 -07001900
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001901 if url.startswith('..'):
1902 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001903 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001904 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001905 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001906 review=self.remote.review,
1907 revision=self.remote.revision)
1908 subproject = Project(manifest=self.manifest,
1909 name=name,
1910 remote=remote,
1911 gitdir=gitdir,
1912 objdir=objdir,
1913 worktree=worktree,
1914 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001915 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001916 revisionId=rev,
1917 rebase=self.rebase,
1918 groups=self.groups,
1919 sync_c=self.sync_c,
1920 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001921 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001922 parent=self,
1923 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001924 result.append(subproject)
1925 result.extend(subproject.GetDerivedSubprojects())
1926 return result
1927
1928
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001929# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001930 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001931 try:
1932 # if revision (sha or tag) is not present then following function
1933 # throws an error.
1934 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1935 return True
1936 except GitError:
1937 # There is no such persistent revision. We have to fetch it.
1938 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001939
Julien Campergue335f5ef2013-10-16 11:02:35 +02001940 def _FetchArchive(self, tarpath, cwd=None):
1941 cmd = ['archive', '-v', '-o', tarpath]
1942 cmd.append('--remote=%s' % self.remote.url)
1943 cmd.append('--prefix=%s/' % self.relpath)
1944 cmd.append(self.revisionExpr)
1945
1946 command = GitCommand(self, cmd, cwd=cwd,
1947 capture_stdout=True,
1948 capture_stderr=True)
1949
1950 if command.Wait() != 0:
1951 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1952
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001953 def _RemoteFetch(self, name=None,
1954 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001955 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001956 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001957 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001958 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001959 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001960 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001961 submodules=False,
1962 force_sync=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001963
1964 is_sha1 = False
1965 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001966 # The depth should not be used when fetching to a mirror because
1967 # it will result in a shallow repository that cannot be cloned or
1968 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001969 # The repo project should also never be synced with partial depth.
1970 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1971 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001972
Shawn Pearce69e04d82014-01-29 12:48:54 -08001973 if depth:
1974 current_branch_only = True
1975
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001976 if ID_RE.match(self.revisionExpr) is not None:
1977 is_sha1 = True
1978
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001979 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001980 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001981 # this is a tag and its sha1 value should never change
1982 tag_name = self.revisionExpr[len(R_TAGS):]
1983
1984 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001985 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001986 if not quiet:
1987 print('Skipped fetching project %s (already have persistent ref)'
1988 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001989 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001990 if is_sha1 and not depth:
1991 # When syncing a specific commit and --depth is not set:
1992 # * if upstream is explicitly specified and is not a sha1, fetch only
1993 # upstream as users expect only upstream to be fetch.
1994 # Note: The commit might not be in upstream in which case the sync
1995 # will fail.
1996 # * otherwise, fetch all branches to make sure we end up with the
1997 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001998 if self.upstream:
1999 current_branch_only = not ID_RE.match(self.upstream)
2000 else:
2001 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002002
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002003 if not name:
2004 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002005
2006 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002007 remote = self.GetRemote(name)
2008 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002009 ssh_proxy = True
2010
Shawn O. Pearce88443382010-10-08 10:02:09 +02002011 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002012 if alt_dir and 'objects' == os.path.basename(alt_dir):
2013 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2015 remote = self.GetRemote(name)
2016
David Pursehouse8a68ff92012-09-24 12:15:13 +09002017 all_refs = self.bare_ref.all
2018 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 tmp = set()
2020
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302021 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002022 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002023 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002024 all_refs[r] = ref_id
2025 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002026 continue
2027
David Pursehouse8a68ff92012-09-24 12:15:13 +09002028 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002029 continue
2030
David Pursehouse8a68ff92012-09-24 12:15:13 +09002031 r = 'refs/_alt/%s' % ref_id
2032 all_refs[r] = ref_id
2033 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034 tmp.add(r)
2035
heping3d7bbc92017-04-12 19:51:47 +08002036 tmp_packed_lines = []
2037 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302039 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002040 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002041 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002042 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002043 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002044
heping3d7bbc92017-04-12 19:51:47 +08002045 tmp_packed = ''.join(tmp_packed_lines)
2046 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002047 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002048 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002049 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002051 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002052
Conley Owensf97e8382015-01-21 11:12:46 -08002053 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002054 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002055 else:
2056 # If this repo has shallow objects, then we don't know which refs have
2057 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2058 # do this with projects that don't have shallow objects, since it is less
2059 # efficient.
2060 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2061 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002062
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002063 if quiet:
2064 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002065 if not self.worktree:
2066 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002067 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002068
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002069 # If using depth then we should not get all the tags since they may
2070 # be outside of the depth.
2071 if no_tags or depth:
2072 cmd.append('--no-tags')
2073 else:
2074 cmd.append('--tags')
2075
Mike Frysingere57f1142019-03-18 21:27:54 -04002076 if force_sync:
2077 cmd.append('--force')
2078
David Pursehouse74cfd272015-10-14 10:50:15 +09002079 if prune:
2080 cmd.append('--prune')
2081
Martin Kellye4e94d22017-03-21 16:05:12 -07002082 if submodules:
2083 cmd.append('--recurse-submodules=on-demand')
2084
Conley Owens80b87fe2014-05-09 17:13:44 -07002085 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002086 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002088 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002089 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002090 spec.append('tag')
2091 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002092
David Pursehouse403b64e2015-04-27 10:41:33 +09002093 if not self.manifest.IsMirror:
2094 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002095 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002096 # Shallow checkout of a specific commit, fetch from that commit and not
2097 # the heads only as the commit might be deeper in the history.
2098 spec.append(branch)
2099 else:
2100 if is_sha1:
2101 branch = self.upstream
2102 if branch is not None and branch.strip():
2103 if not branch.startswith('refs/'):
2104 branch = R_HEADS + branch
2105 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002106 cmd.extend(spec)
2107
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002108 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002109 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002110 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002111 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002112 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002113 ok = True
2114 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002115 # If needed, run the 'git remote prune' the first time through the loop
2116 elif (not _i and
2117 "error:" in gitcmd.stderr and
2118 "git remote prune" in gitcmd.stderr):
2119 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002120 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002121 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002122 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002123 break
2124 continue
Brian Harring14a66742012-09-28 20:21:57 -07002125 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002126 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2127 # in sha1 mode, we just tried sync'ing from the upstream field; it
2128 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002129 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002130 elif ret < 0:
2131 # Git died with a signal, exit immediately
2132 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002133 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134
2135 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002136 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002137 if old_packed != '':
2138 _lwrite(packed_refs, old_packed)
2139 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002140 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002141 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002142
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002143 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002144 # We just synced the upstream given branch; verify we
2145 # got what we wanted, else trigger a second run of all
2146 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002147 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002148 if current_branch_only and depth:
2149 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002150 return self._RemoteFetch(name=name,
2151 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002152 initial=False, quiet=quiet, alt_dir=alt_dir,
2153 depth=None)
2154 else:
2155 # Avoid infinite recursion: sync all branches with depth set to None
2156 return self._RemoteFetch(name=name, current_branch_only=False,
2157 initial=False, quiet=quiet, alt_dir=alt_dir,
2158 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002159
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002160 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002161
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002162 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002163 if initial and \
2164 (self.manifest.manifestProject.config.GetString('repo.depth') or
2165 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002166 return False
2167
2168 remote = self.GetRemote(self.remote.name)
2169 bundle_url = remote.url + '/clone.bundle'
2170 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002171 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2172 'persistent-http',
2173 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002174 return False
2175
2176 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2177 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2178
2179 exist_dst = os.path.exists(bundle_dst)
2180 exist_tmp = os.path.exists(bundle_tmp)
2181
2182 if not initial and not exist_dst and not exist_tmp:
2183 return False
2184
2185 if not exist_dst:
2186 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2187 if not exist_dst:
2188 return False
2189
2190 cmd = ['fetch']
2191 if quiet:
2192 cmd.append('--quiet')
2193 if not self.worktree:
2194 cmd.append('--update-head-ok')
2195 cmd.append(bundle_dst)
2196 for f in remote.fetch:
2197 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002198 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002199
2200 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002201 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002202 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002203 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002204 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002205 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002206
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002207 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002208 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002209 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002210
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002211 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002212 if quiet:
2213 cmd += ['--silent']
2214 if os.path.exists(tmpPath):
2215 size = os.stat(tmpPath).st_size
2216 if size >= 1024:
2217 cmd += ['--continue-at', '%d' % (size,)]
2218 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002219 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002220 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002221 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002222 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002223 if proxy:
2224 cmd += ['--proxy', proxy]
2225 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2226 cmd += ['--proxy', os.environ['http_proxy']]
2227 if srcUrl.startswith('persistent-https'):
2228 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2229 elif srcUrl.startswith('persistent-http'):
2230 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002231 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002232
Dave Borowitz137d0132015-01-02 11:12:54 -08002233 if IsTrace():
2234 Trace('%s', ' '.join(cmd))
2235 try:
2236 proc = subprocess.Popen(cmd)
2237 except OSError:
2238 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002239
Dave Borowitz137d0132015-01-02 11:12:54 -08002240 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002241
Dave Borowitz137d0132015-01-02 11:12:54 -08002242 if curlret == 22:
2243 # From curl man page:
2244 # 22: HTTP page not retrieved. The requested url was not found or
2245 # returned another error with the HTTP error code being 400 or above.
2246 # This return code only appears if -f, --fail is used.
2247 if not quiet:
2248 print("Server does not provide clone.bundle; ignoring.",
2249 file=sys.stderr)
2250 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002251
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002252 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002253 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002254 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002255 return True
2256 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002257 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002258 return False
2259 else:
2260 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002261
Kris Giesingc8d882a2014-12-23 13:02:32 -08002262 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002263 try:
2264 with open(path) as f:
2265 if f.read(16) == '# v2 git bundle\n':
2266 return True
2267 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002268 if not quiet:
2269 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002270 return False
2271 except OSError:
2272 return False
2273
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002274 def _Checkout(self, rev, quiet=False):
2275 cmd = ['checkout']
2276 if quiet:
2277 cmd.append('-q')
2278 cmd.append(rev)
2279 cmd.append('--')
2280 if GitCommand(self, cmd).Wait() != 0:
2281 if self._allrefs:
2282 raise GitError('%s checkout %s ' % (self.name, rev))
2283
Anthony King7bdac712014-07-16 12:56:40 +01002284 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002285 cmd = ['cherry-pick']
2286 cmd.append(rev)
2287 cmd.append('--')
2288 if GitCommand(self, cmd).Wait() != 0:
2289 if self._allrefs:
2290 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2291
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302292 def _LsRemote(self, refs):
2293 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302294 p = GitCommand(self, cmd, capture_stdout=True)
2295 if p.Wait() == 0:
2296 if hasattr(p.stdout, 'decode'):
2297 return p.stdout.decode('utf-8')
2298 else:
2299 return p.stdout
2300 return None
2301
Anthony King7bdac712014-07-16 12:56:40 +01002302 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002303 cmd = ['revert']
2304 cmd.append('--no-edit')
2305 cmd.append(rev)
2306 cmd.append('--')
2307 if GitCommand(self, cmd).Wait() != 0:
2308 if self._allrefs:
2309 raise GitError('%s revert %s ' % (self.name, rev))
2310
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002311 def _ResetHard(self, rev, quiet=True):
2312 cmd = ['reset', '--hard']
2313 if quiet:
2314 cmd.append('-q')
2315 cmd.append(rev)
2316 if GitCommand(self, cmd).Wait() != 0:
2317 raise GitError('%s reset --hard %s ' % (self.name, rev))
2318
Martin Kellye4e94d22017-03-21 16:05:12 -07002319 def _SyncSubmodules(self, quiet=True):
2320 cmd = ['submodule', 'update', '--init', '--recursive']
2321 if quiet:
2322 cmd.append('-q')
2323 if GitCommand(self, cmd).Wait() != 0:
2324 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2325
Anthony King7bdac712014-07-16 12:56:40 +01002326 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002327 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002328 if onto is not None:
2329 cmd.extend(['--onto', onto])
2330 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002331 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002332 raise GitError('%s rebase %s ' % (self.name, upstream))
2333
Pierre Tardy3d125942012-05-04 12:18:12 +02002334 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002335 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002336 if ffonly:
2337 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002338 if GitCommand(self, cmd).Wait() != 0:
2339 raise GitError('%s merge %s ' % (self.name, head))
2340
Kevin Degiabaa7f32014-11-12 11:27:45 -07002341 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002342 init_git_dir = not os.path.exists(self.gitdir)
2343 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002344 try:
2345 # Initialize the bare repository, which contains all of the objects.
2346 if init_obj_dir:
2347 os.makedirs(self.objdir)
2348 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002349
Kevin Degib1a07b82015-07-27 13:33:43 -06002350 # If we have a separate directory to hold refs, initialize it as well.
2351 if self.objdir != self.gitdir:
2352 if init_git_dir:
2353 os.makedirs(self.gitdir)
2354
2355 if init_obj_dir or init_git_dir:
2356 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2357 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002358 try:
2359 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2360 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002361 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002362 print("Retrying clone after deleting %s" %
2363 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002364 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002365 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2366 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002367 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002368 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002369 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2370 except:
2371 raise e
2372 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002373
Kevin Degi384b3c52014-10-16 16:02:58 -06002374 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002375 mp = self.manifest.manifestProject
2376 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002377
Kevin Degib1a07b82015-07-27 13:33:43 -06002378 if ref_dir or mirror_git:
2379 if not mirror_git:
2380 mirror_git = os.path.join(ref_dir, self.name + '.git')
2381 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2382 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002383
Kevin Degib1a07b82015-07-27 13:33:43 -06002384 if os.path.exists(mirror_git):
2385 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002386
Kevin Degib1a07b82015-07-27 13:33:43 -06002387 elif os.path.exists(repo_git):
2388 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002389
Kevin Degib1a07b82015-07-27 13:33:43 -06002390 else:
2391 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002392
Kevin Degib1a07b82015-07-27 13:33:43 -06002393 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002394 if not os.path.isabs(ref_dir):
2395 # The alternate directory is relative to the object database.
2396 ref_dir = os.path.relpath(ref_dir,
2397 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002398 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2399 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002400
Kevin Degib1a07b82015-07-27 13:33:43 -06002401 self._UpdateHooks()
2402
2403 m = self.manifest.manifestProject.config
2404 for key in ['user.name', 'user.email']:
2405 if m.Has(key, include_defaults=False):
2406 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002407 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002408 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002409 if self.manifest.IsMirror:
2410 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002411 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002412 self.config.SetString('core.bare', None)
2413 except Exception:
2414 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002415 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002416 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002417 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002418 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002419
Jimmie Westera0444582012-10-24 13:44:42 +02002420 def _UpdateHooks(self):
2421 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002422 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002423
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002424 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002425 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002426 if not os.path.exists(hooks):
2427 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002428 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002429 name = os.path.basename(stock_hook)
2430
Victor Boivie65e0f352011-04-18 11:23:29 +02002431 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002432 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002433 # Don't install a Gerrit Code Review hook if this
2434 # project does not appear to use it for reviews.
2435 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002436 # Since the manifest project is one of those, but also
2437 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002438 continue
2439
2440 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002441 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002442 continue
2443 if os.path.exists(dst):
2444 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002445 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002446 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002447 _warn("%s: Not replacing locally modified %s hook",
2448 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002449 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002450 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002451 platform_utils.symlink(
2452 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002453 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002454 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002455 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002456 else:
2457 raise
2458
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002459 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002460 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002461 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002462 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002463 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002464 remote.review = self.remote.review
2465 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002466
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002467 if self.worktree:
2468 remote.ResetFetch(mirror=False)
2469 else:
2470 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002471 remote.Save()
2472
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002473 def _InitMRef(self):
2474 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002475 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002476
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002477 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002478 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002479
2480 def _InitAnyMRef(self, ref):
2481 cur = self.bare_ref.symref(ref)
2482
2483 if self.revisionId:
2484 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2485 msg = 'manifest set to %s' % self.revisionId
2486 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002487 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002488 else:
2489 remote = self.GetRemote(self.remote.name)
2490 dst = remote.ToLocal(self.revisionExpr)
2491 if cur != dst:
2492 msg = 'manifest set to %s' % self.revisionExpr
2493 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002494
Kevin Degi384b3c52014-10-16 16:02:58 -06002495 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002496 symlink_files = self.shareable_files[:]
2497 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002498 if share_refs:
2499 symlink_files += self.working_tree_files
2500 symlink_dirs += self.working_tree_dirs
2501 to_symlink = symlink_files + symlink_dirs
2502 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002503 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002504 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002505 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002506 # Fail if the links are pointing to the wrong place
2507 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002508 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002509 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002510 'work tree. If you\'re comfortable with the '
2511 'possibility of losing the work tree\'s git metadata,'
2512 ' use `repo sync --force-sync {0}` to '
2513 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002514
David James8d201162013-10-11 17:03:19 -07002515 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2516 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2517
2518 Args:
2519 gitdir: The bare git repository. Must already be initialized.
2520 dotgit: The repository you would like to initialize.
2521 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2522 Only one work tree can store refs under a given |gitdir|.
2523 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2524 This saves you the effort of initializing |dotgit| yourself.
2525 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002526 symlink_files = self.shareable_files[:]
2527 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002528 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002529 symlink_files += self.working_tree_files
2530 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002531 to_symlink = symlink_files + symlink_dirs
2532
2533 to_copy = []
2534 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002535 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002536
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002537 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002538 for name in set(to_copy).union(to_symlink):
2539 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002540 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002541 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002542
Kevin Degi384b3c52014-10-16 16:02:58 -06002543 if os.path.lexists(dst):
2544 continue
David James8d201162013-10-11 17:03:19 -07002545
2546 # If the source dir doesn't exist, create an empty dir.
2547 if name in symlink_dirs and not os.path.lexists(src):
2548 os.makedirs(src)
2549
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002550 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002551 platform_utils.symlink(
2552 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002553 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002554 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002555 shutil.copytree(src, dst)
2556 elif os.path.isfile(src):
2557 shutil.copy(src, dst)
2558
Conley Owens80b87fe2014-05-09 17:13:44 -07002559 # If the source file doesn't exist, ensure the destination
2560 # file doesn't either.
2561 if name in symlink_files and not os.path.lexists(src):
2562 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002563 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002564 except OSError:
2565 pass
2566
David James8d201162013-10-11 17:03:19 -07002567 except OSError as e:
2568 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002569 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002570 else:
2571 raise
2572
Martin Kellye4e94d22017-03-21 16:05:12 -07002573 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002574 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002575 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002576 try:
2577 if init_dotgit:
2578 os.makedirs(dotgit)
2579 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2580 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002581
Kevin Degiabaa7f32014-11-12 11:27:45 -07002582 try:
2583 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2584 except GitError as e:
2585 if force_sync:
2586 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002587 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002588 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002589 except:
2590 raise e
2591 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002592
Kevin Degib1a07b82015-07-27 13:33:43 -06002593 if init_dotgit:
2594 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595
Kevin Degib1a07b82015-07-27 13:33:43 -06002596 cmd = ['read-tree', '--reset', '-u']
2597 cmd.append('-v')
2598 cmd.append(HEAD)
2599 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002600 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002601
Martin Kellye4e94d22017-03-21 16:05:12 -07002602 if submodules:
2603 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002604 self._CopyAndLinkFiles()
2605 except Exception:
2606 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002607 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002608 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002609
Renaud Paquay788e9622017-01-27 11:41:12 -08002610 def _get_symlink_error_message(self):
2611 if platform_utils.isWindows():
2612 return ('Unable to create symbolic link. Please re-run the command as '
2613 'Administrator, or see '
2614 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2615 'for other options.')
2616 return 'filesystem must support symlinks'
2617
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002618 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002619 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002620
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002621 def _revlist(self, *args, **kw):
2622 a = []
2623 a.extend(args)
2624 a.append('--')
2625 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002626
2627 @property
2628 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002629 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002630
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002631 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002632 """Get logs between two revisions of this project."""
2633 comp = '..'
2634 if rev1:
2635 revs = [rev1]
2636 if rev2:
2637 revs.extend([comp, rev2])
2638 cmd = ['log', ''.join(revs)]
2639 out = DiffColoring(self.config)
2640 if out.is_on and color:
2641 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002642 if pretty_format is not None:
2643 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002644 if oneline:
2645 cmd.append('--oneline')
2646
2647 try:
2648 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2649 if log.Wait() == 0:
2650 return log.stdout
2651 except GitError:
2652 # worktree may not exist if groups changed for example. In that case,
2653 # try in gitdir instead.
2654 if not os.path.exists(self.worktree):
2655 return self.bare_git.log(*cmd[1:])
2656 else:
2657 raise
2658 return None
2659
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002660 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2661 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002662 """Get the list of logs from this revision to given revisionId"""
2663 logs = {}
2664 selfId = self.GetRevisionId(self._allrefs)
2665 toId = toProject.GetRevisionId(toProject._allrefs)
2666
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002667 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2668 pretty_format=pretty_format)
2669 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2670 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002671 return logs
2672
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002673 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002674
David James8d201162013-10-11 17:03:19 -07002675 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002676 self._project = project
2677 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002678 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002679
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002680 def LsOthers(self):
2681 p = GitCommand(self._project,
2682 ['ls-files',
2683 '-z',
2684 '--others',
2685 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002686 bare=False,
David James8d201162013-10-11 17:03:19 -07002687 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002688 capture_stdout=True,
2689 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002690 if p.Wait() == 0:
2691 out = p.stdout
2692 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002693 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002694 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002695 return []
2696
2697 def DiffZ(self, name, *args):
2698 cmd = [name]
2699 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002700 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002701 cmd.extend(args)
2702 p = GitCommand(self._project,
2703 cmd,
David James8d201162013-10-11 17:03:19 -07002704 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002705 bare=False,
2706 capture_stdout=True,
2707 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002708 try:
2709 out = p.process.stdout.read()
2710 r = {}
2711 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002712 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002713 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002714 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002715 info = next(out)
2716 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002717 except StopIteration:
2718 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002719
2720 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002721
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002722 def __init__(self, path, omode, nmode, oid, nid, state):
2723 self.path = path
2724 self.src_path = None
2725 self.old_mode = omode
2726 self.new_mode = nmode
2727 self.old_id = oid
2728 self.new_id = nid
2729
2730 if len(state) == 1:
2731 self.status = state
2732 self.level = None
2733 else:
2734 self.status = state[:1]
2735 self.level = state[1:]
2736 while self.level.startswith('0'):
2737 self.level = self.level[1:]
2738
2739 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002740 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741 if info.status in ('R', 'C'):
2742 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002743 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002744 r[info.path] = info
2745 return r
2746 finally:
2747 p.Wait()
2748
2749 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002750 if self._bare:
2751 path = os.path.join(self._project.gitdir, HEAD)
2752 else:
2753 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002754 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002755 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002756 except IOError as e:
2757 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002758 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002759 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002760 finally:
2761 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302762 try:
2763 line = line.decode()
2764 except AttributeError:
2765 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002766 if line.startswith('ref: '):
2767 return line[5:-1]
2768 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002769
2770 def SetHead(self, ref, message=None):
2771 cmdv = []
2772 if message is not None:
2773 cmdv.extend(['-m', message])
2774 cmdv.append(HEAD)
2775 cmdv.append(ref)
2776 self.symbolic_ref(*cmdv)
2777
2778 def DetachHead(self, new, message=None):
2779 cmdv = ['--no-deref']
2780 if message is not None:
2781 cmdv.extend(['-m', message])
2782 cmdv.append(HEAD)
2783 cmdv.append(new)
2784 self.update_ref(*cmdv)
2785
2786 def UpdateRef(self, name, new, old=None,
2787 message=None,
2788 detach=False):
2789 cmdv = []
2790 if message is not None:
2791 cmdv.extend(['-m', message])
2792 if detach:
2793 cmdv.append('--no-deref')
2794 cmdv.append(name)
2795 cmdv.append(new)
2796 if old is not None:
2797 cmdv.append(old)
2798 self.update_ref(*cmdv)
2799
2800 def DeleteRef(self, name, old=None):
2801 if not old:
2802 old = self.rev_parse(name)
2803 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002804 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002805
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002806 def rev_list(self, *args, **kw):
2807 if 'format' in kw:
2808 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2809 else:
2810 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002811 cmdv.extend(args)
2812 p = GitCommand(self._project,
2813 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002814 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002815 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002816 capture_stdout=True,
2817 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002818 r = []
2819 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002820 if line[-1] == '\n':
2821 line = line[:-1]
2822 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002823 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002824 raise GitError('%s rev-list %s: %s' %
2825 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002826 return r
2827
2828 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002829 """Allow arbitrary git commands using pythonic syntax.
2830
2831 This allows you to do things like:
2832 git_obj.rev_parse('HEAD')
2833
2834 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2835 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002836 Any other positional arguments will be passed to the git command, and the
2837 following keyword arguments are supported:
2838 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002839
2840 Args:
2841 name: The name of the git command to call. Any '_' characters will
2842 be replaced with '-'.
2843
2844 Returns:
2845 A callable object that will try to call git with the named command.
2846 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002847 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002848
Dave Borowitz091f8932012-10-23 17:01:04 -07002849 def runner(*args, **kwargs):
2850 cmdv = []
2851 config = kwargs.pop('config', None)
2852 for k in kwargs:
2853 raise TypeError('%s() got an unexpected keyword argument %r'
2854 % (name, k))
2855 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002856 if not git_require((1, 7, 2)):
2857 raise ValueError('cannot set config on command line for %s()'
2858 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302859 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002860 cmdv.append('-c')
2861 cmdv.append('%s=%s' % (k, v))
2862 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002863 cmdv.extend(args)
2864 p = GitCommand(self._project,
2865 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002866 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002867 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002868 capture_stdout=True,
2869 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002870 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002871 raise GitError('%s %s: %s' %
2872 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002873 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302874 try:
Conley Owensedd01512013-09-26 12:59:58 -07002875 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302876 except AttributeError:
2877 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002878 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2879 return r[:-1]
2880 return r
2881 return runner
2882
2883
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002884class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002885
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002886 def __str__(self):
2887 return 'prior sync failed; rebase still in progress'
2888
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002889
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002890class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002891
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002892 def __str__(self):
2893 return 'contains uncommitted changes'
2894
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002895
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002896class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002897
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002898 def __init__(self, project, text):
2899 self.project = project
2900 self.text = text
2901
2902 def Print(self, syncbuf):
2903 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2904 syncbuf.out.nl()
2905
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002906
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002907class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002908
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002909 def __init__(self, project, why):
2910 self.project = project
2911 self.why = why
2912
2913 def Print(self, syncbuf):
2914 syncbuf.out.fail('error: %s/: %s',
2915 self.project.relpath,
2916 str(self.why))
2917 syncbuf.out.nl()
2918
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002919
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002920class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002921
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002922 def __init__(self, project, action):
2923 self.project = project
2924 self.action = action
2925
2926 def Run(self, syncbuf):
2927 out = syncbuf.out
2928 out.project('project %s/', self.project.relpath)
2929 out.nl()
2930 try:
2931 self.action()
2932 out.nl()
2933 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002934 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002935 out.nl()
2936 return False
2937
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002938
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002939class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002940
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002941 def __init__(self, config):
2942 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002943 self.project = self.printer('header', attr='bold')
2944 self.info = self.printer('info')
2945 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002946
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002947
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002948class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002949
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002950 def __init__(self, config, detach_head=False):
2951 self._messages = []
2952 self._failures = []
2953 self._later_queue1 = []
2954 self._later_queue2 = []
2955
2956 self.out = _SyncColoring(config)
2957 self.out.redirect(sys.stderr)
2958
2959 self.detach_head = detach_head
2960 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002961 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002962
2963 def info(self, project, fmt, *args):
2964 self._messages.append(_InfoMessage(project, fmt % args))
2965
2966 def fail(self, project, err=None):
2967 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002968 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002969
2970 def later1(self, project, what):
2971 self._later_queue1.append(_Later(project, what))
2972
2973 def later2(self, project, what):
2974 self._later_queue2.append(_Later(project, what))
2975
2976 def Finish(self):
2977 self._PrintMessages()
2978 self._RunLater()
2979 self._PrintMessages()
2980 return self.clean
2981
David Rileye0684ad2017-04-05 00:02:59 -07002982 def Recently(self):
2983 recent_clean = self.recent_clean
2984 self.recent_clean = True
2985 return recent_clean
2986
2987 def _MarkUnclean(self):
2988 self.clean = False
2989 self.recent_clean = False
2990
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002991 def _RunLater(self):
2992 for q in ['_later_queue1', '_later_queue2']:
2993 if not self._RunQueue(q):
2994 return
2995
2996 def _RunQueue(self, queue):
2997 for m in getattr(self, queue):
2998 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002999 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003000 return False
3001 setattr(self, queue, [])
3002 return True
3003
3004 def _PrintMessages(self):
3005 for m in self._messages:
3006 m.Print(self)
3007 for m in self._failures:
3008 m.Print(self)
3009
3010 self._messages = []
3011 self._failures = []
3012
3013
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003014class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003015
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003016 """A special project housed under .repo.
3017 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003018
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003019 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003020 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003021 manifest=manifest,
3022 name=name,
3023 gitdir=gitdir,
3024 objdir=gitdir,
3025 worktree=worktree,
3026 remote=RemoteSpec('origin'),
3027 relpath='.repo/%s' % name,
3028 revisionExpr='refs/heads/master',
3029 revisionId=None,
3030 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003031
3032 def PreSync(self):
3033 if self.Exists:
3034 cb = self.CurrentBranch
3035 if cb:
3036 base = self.GetBranch(cb).merge
3037 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003038 self.revisionExpr = base
3039 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003040
Martin Kelly224a31a2017-07-10 14:46:25 -07003041 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003042 """ Prepare MetaProject for manifest branch switch
3043 """
3044
3045 # detach and delete manifest branch, allowing a new
3046 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003047 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003048 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003049 syncbuf.Finish()
3050
3051 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003052 ['update-ref', '-d', 'refs/heads/default'],
3053 capture_stdout=True,
3054 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003055
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003056 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003057 def LastFetch(self):
3058 try:
3059 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3060 return os.path.getmtime(fh)
3061 except OSError:
3062 return 0
3063
3064 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003065 def HasChanges(self):
3066 """Has the remote received new commits not yet checked out?
3067 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003068 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003069 return False
3070
David Pursehouse8a68ff92012-09-24 12:15:13 +09003071 all_refs = self.bare_ref.all
3072 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003073 head = self.work_git.GetHead()
3074 if head.startswith(R_HEADS):
3075 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003076 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003077 except KeyError:
3078 head = None
3079
3080 if revid == head:
3081 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003082 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003083 return True
3084 return False