blob: c9679d703aaec3004f952ea56283792979cb52fe [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070034 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070036from error import CacheApplyError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080037from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070040from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041
Shawn O. Pearced237b692009-04-17 18:49:50 -070042from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
David Pursehouse59bbb582013-05-17 10:49:33 +090044from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040045if is_python3():
46 import urllib.parse
47else:
48 import imp
49 import urlparse
50 urllib = imp.new_module('urllib')
51 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070054
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055def _lwrite(path, content):
56 lock = '%s.lock' % path
57
Chirayu Desai303a82f2014-08-19 22:57:17 +053058 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 try:
60 fd.write(content)
61 finally:
62 fd.close()
63
64 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070065 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070066 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080067 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 raise
69
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
Shawn O. Pearce48244782009-04-16 08:25:57 -070071def _error(fmt, *args):
72 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070073 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
David Pursehousef33929d2015-08-24 14:39:14 +090076def _warn(fmt, *args):
77 msg = fmt % args
78 print('warn: %s' % msg, file=sys.stderr)
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081def not_rev(r):
82 return '^' + r
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080085def sq(r):
86 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080087
Jonathan Nieder93719792015-03-17 11:29:58 -070088_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
90
Jonathan Nieder93719792015-03-17 11:29:58 -070091def _ProjectHooks():
92 """List the hooks present in the 'hooks' directory.
93
94 These hooks are project hooks and are copied to the '.git/hooks' directory
95 of all subprojects.
96
97 This function caches the list of hooks (based on the contents of the
98 'repo/hooks' directory) on the first call.
99
100 Returns:
101 A list of absolute paths to all of the files in the hooks directory.
102 """
103 global _project_hook_list
104 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700105 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700106 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700107 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 return _project_hook_list
109
110
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700111class DownloadedChange(object):
112 _commit_cache = None
113
114 def __init__(self, project, base, change_id, ps_id, commit):
115 self.project = project
116 self.base = base
117 self.change_id = change_id
118 self.ps_id = ps_id
119 self.commit = commit
120
121 @property
122 def commits(self):
123 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700124 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
125 '--abbrev-commit',
126 '--pretty=oneline',
127 '--reverse',
128 '--date-order',
129 not_rev(self.base),
130 self.commit,
131 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700132 return self._commit_cache
133
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135class ReviewableBranch(object):
136 _commit_cache = None
137
138 def __init__(self, project, branch, base):
139 self.project = project
140 self.branch = branch
141 self.base = base
142
143 @property
144 def name(self):
145 return self.branch.name
146
147 @property
148 def commits(self):
149 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700150 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
151 '--abbrev-commit',
152 '--pretty=oneline',
153 '--reverse',
154 '--date-order',
155 not_rev(self.base),
156 R_HEADS + self.name,
157 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700176 def UploadForReview(self, people,
177 auto_topic=False,
178 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200179 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700180 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700190 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200192 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800193 validate_certs=validate_certs,
194 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700195
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700196 def GetPublishedRefs(self):
197 refs = {}
198 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700199 self.branch.remote.SshReviewUrl(self.project.UserEmail),
200 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700201 for line in output.split('\n'):
202 try:
203 (sha, ref) = line.split()
204 refs[sha] = ref
205 except ValueError:
206 pass
207
208 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700209
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700210
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213 def __init__(self, config):
214 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100215 self.project = self.printer('header', attr='bold')
216 self.branch = self.printer('header', attr='bold')
217 self.nobranch = self.printer('nobranch', fg='red')
218 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219
Anthony King7bdac712014-07-16 12:56:40 +0100220 self.added = self.printer('added', fg='green')
221 self.changed = self.printer('changed', fg='red')
222 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700223
224
225class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700226
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227 def __init__(self, config):
228 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100229 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
Anthony King7bdac712014-07-16 12:56:40 +0100232class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
James W. Mills24c13082012-04-12 15:04:13 -0500234 def __init__(self, name, value, keep):
235 self.name = name
236 self.value = value
237 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700239
Anthony King7bdac712014-07-16 12:56:40 +0100240class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243 self.src = src
244 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800245 self.abs_src = abssrc
246 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247
248 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800249 src = self.abs_src
250 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 # copy file if it does not exist or is out of date
252 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
253 try:
254 # remove existing file first, since it might be read-only
255 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800256 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400257 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200258 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700259 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700261 shutil.copy(src, dest)
262 # make the file read-only
263 mode = os.stat(dest)[stat.ST_MODE]
264 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
265 os.chmod(dest, mode)
266 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700267 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700269
Anthony King7bdac712014-07-16 12:56:40 +0100270class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Wink Saville4c426ef2015-06-03 08:05:17 -0700272 def __init__(self, git_worktree, src, dest, relsrc, absdest):
273 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 self.src = src
275 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700276 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 self.abs_dest = absdest
278
Wink Saville4c426ef2015-06-03 08:05:17 -0700279 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500280 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700281 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 try:
283 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800284 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800285 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700288 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500289 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700290 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500291 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700292 _error('Cannot link file %s to %s', relSrc, absDest)
293
294 def _Link(self):
295 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
296 on the src linking all of the files in the source in to the destination
297 directory.
298 """
299 # We use the absSrc to handle the situation where the current directory
300 # is not the root of the repo
301 absSrc = os.path.join(self.git_worktree, self.src)
302 if os.path.exists(absSrc):
303 # Entity exists so just a simple one to one link operation
304 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
305 else:
306 # Entity doesn't exist assume there is a wild card
307 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700308 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700309 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700310 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 else:
312 absSrcFiles = glob.glob(absSrc)
313 for absSrcFile in absSrcFiles:
314 # Create a releative path from source dir to destination dir
315 absSrcDir = os.path.dirname(absSrcFile)
316 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
317
318 # Get the source file name
319 srcFile = os.path.basename(absSrcFile)
320
321 # Now form the final full paths to srcFile. They will be
322 # absolute for the desintaiton and relative for the srouce.
323 absDest = os.path.join(absDestDir, srcFile)
324 relSrc = os.path.join(relSrcDir, srcFile)
325 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500326
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700327
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700328class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330 def __init__(self,
331 name,
Anthony King7bdac712014-07-16 12:56:40 +0100332 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700333 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100334 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700335 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700336 orig_name=None,
337 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700338 self.name = name
339 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700340 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700341 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100342 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700343 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700344 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700345
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700346
Doug Anderson37282b42011-03-04 11:54:18 -0800347class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349 """A RepoHook contains information about a script to run as a hook.
350
351 Hooks are used to run a python script before running an upload (for instance,
352 to run presubmit checks). Eventually, we may have hooks for other actions.
353
354 This shouldn't be confused with files in the 'repo/hooks' directory. Those
355 files are copied into each '.git/hooks' folder for each project. Repo-level
356 hooks are associated instead with repo actions.
357
358 Hooks are always python. When a hook is run, we will load the hook into the
359 interpreter and execute its main() function.
360 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700361
Doug Anderson37282b42011-03-04 11:54:18 -0800362 def __init__(self,
363 hook_type,
364 hooks_project,
365 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400366 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800367 abort_if_user_denies=False):
368 """RepoHook constructor.
369
370 Params:
371 hook_type: A string representing the type of hook. This is also used
372 to figure out the name of the file containing the hook. For
373 example: 'pre-upload'.
374 hooks_project: The project containing the repo hooks. If you have a
375 manifest, this is manifest.repo_hooks_project. OK if this is None,
376 which will make the hook a no-op.
377 topdir: Repo's top directory (the one containing the .repo directory).
378 Scripts will run with CWD as this directory. If you have a manifest,
379 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400380 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800381 abort_if_user_denies: If True, we'll throw a HookError() if the user
382 doesn't allow us to run the hook.
383 """
384 self._hook_type = hook_type
385 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400386 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800387 self._topdir = topdir
388 self._abort_if_user_denies = abort_if_user_denies
389
390 # Store the full path to the script for convenience.
391 if self._hooks_project:
392 self._script_fullpath = os.path.join(self._hooks_project.worktree,
393 self._hook_type + '.py')
394 else:
395 self._script_fullpath = None
396
397 def _GetHash(self):
398 """Return a hash of the contents of the hooks directory.
399
400 We'll just use git to do this. This hash has the property that if anything
401 changes in the directory we will return a different has.
402
403 SECURITY CONSIDERATION:
404 This hash only represents the contents of files in the hook directory, not
405 any other files imported or called by hooks. Changes to imported files
406 can change the script behavior without affecting the hash.
407
408 Returns:
409 A string representing the hash. This will always be ASCII so that it can
410 be printed to the user easily.
411 """
412 assert self._hooks_project, "Must have hooks to calculate their hash."
413
414 # We will use the work_git object rather than just calling GetRevisionId().
415 # That gives us a hash of the latest checked in version of the files that
416 # the user will actually be executing. Specifically, GetRevisionId()
417 # doesn't appear to change even if a user checks out a different version
418 # of the hooks repo (via git checkout) nor if a user commits their own revs.
419 #
420 # NOTE: Local (non-committed) changes will not be factored into this hash.
421 # I think this is OK, since we're really only worried about warning the user
422 # about upstream changes.
423 return self._hooks_project.work_git.rev_parse('HEAD')
424
425 def _GetMustVerb(self):
426 """Return 'must' if the hook is required; 'should' if not."""
427 if self._abort_if_user_denies:
428 return 'must'
429 else:
430 return 'should'
431
432 def _CheckForHookApproval(self):
433 """Check to see whether this hook has been approved.
434
Mike Frysinger40252c22016-08-15 21:23:44 -0400435 We'll accept approval of manifest URLs if they're using secure transports.
436 This way the user can say they trust the manifest hoster. For insecure
437 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800438
439 Note that we ask permission for each individual hook even though we use
440 the hash of all hooks when detecting changes. We'd like the user to be
441 able to approve / deny each hook individually. We only use the hash of all
442 hooks because there is no other easy way to detect changes to local imports.
443
444 Returns:
445 True if this hook is approved to run; False otherwise.
446
447 Raises:
448 HookError: Raised if the user doesn't approve and abort_if_user_denies
449 was passed to the consturctor.
450 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400451 if self._ManifestUrlHasSecureScheme():
452 return self._CheckForHookApprovalManifest()
453 else:
454 return self._CheckForHookApprovalHash()
455
456 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
457 changed_prompt):
458 """Check for approval for a particular attribute and hook.
459
460 Args:
461 subkey: The git config key under [repo.hooks.<hook_type>] to store the
462 last approved string.
463 new_val: The new value to compare against the last approved one.
464 main_prompt: Message to display to the user to ask for approval.
465 changed_prompt: Message explaining why we're re-asking for approval.
466
467 Returns:
468 True if this hook is approved to run; False otherwise.
469
470 Raises:
471 HookError: Raised if the user doesn't approve and abort_if_user_denies
472 was passed to the consturctor.
473 """
Doug Anderson37282b42011-03-04 11:54:18 -0800474 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800476
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 # Get the last value that the user approved for this hook; may be None.
478 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800479
Mike Frysinger40252c22016-08-15 21:23:44 -0400480 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800481 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # Approval matched. We're done.
484 return True
485 else:
486 # Give the user a reason why we're prompting, since they last told
487 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400488 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800489 else:
490 prompt = ''
491
492 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
493 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400494 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530495 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900496 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800497
498 # User is doing a one-time approval.
499 if response in ('y', 'yes'):
500 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400501 elif response == 'always':
502 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800503 return True
504
505 # For anything else, we'll assume no approval.
506 if self._abort_if_user_denies:
507 raise HookError('You must allow the %s hook or use --no-verify.' %
508 self._hook_type)
509
510 return False
511
Mike Frysinger40252c22016-08-15 21:23:44 -0400512 def _ManifestUrlHasSecureScheme(self):
513 """Check if the URI for the manifest is a secure transport."""
514 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
515 parse_results = urllib.parse.urlparse(self._manifest_url)
516 return parse_results.scheme in secure_schemes
517
518 def _CheckForHookApprovalManifest(self):
519 """Check whether the user has approved this manifest host.
520
521 Returns:
522 True if this hook is approved to run; False otherwise.
523 """
524 return self._CheckForHookApprovalHelper(
525 'approvedmanifest',
526 self._manifest_url,
527 'Run hook scripts from %s' % (self._manifest_url,),
528 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
529
530 def _CheckForHookApprovalHash(self):
531 """Check whether the user has approved the hooks repo.
532
533 Returns:
534 True if this hook is approved to run; False otherwise.
535 """
536 prompt = ('Repo %s run the script:\n'
537 ' %s\n'
538 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700539 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400540 return self._CheckForHookApprovalHelper(
541 'approvedhash',
542 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700543 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400544 'Scripts have changed since %s was allowed.' % (self._hook_type,))
545
Doug Anderson37282b42011-03-04 11:54:18 -0800546 def _ExecuteHook(self, **kwargs):
547 """Actually execute the given hook.
548
549 This will run the hook's 'main' function in our python interpreter.
550
551 Args:
552 kwargs: Keyword arguments to pass to the hook. These are often specific
553 to the hook type. For instance, pre-upload hooks will contain
554 a project_list.
555 """
556 # Keep sys.path and CWD stashed away so that we can always restore them
557 # upon function exit.
558 orig_path = os.getcwd()
559 orig_syspath = sys.path
560
561 try:
562 # Always run hooks with CWD as topdir.
563 os.chdir(self._topdir)
564
565 # Put the hook dir as the first item of sys.path so hooks can do
566 # relative imports. We want to replace the repo dir as [0] so
567 # hooks can't import repo files.
568 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
569
570 # Exec, storing global context in the context dict. We catch exceptions
571 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500572 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800573 try:
Anthony King70f68902014-05-05 21:15:34 +0100574 exec(compile(open(self._script_fullpath).read(),
575 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800576 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700577 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
578 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800579
580 # Running the script should have defined a main() function.
581 if 'main' not in context:
582 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
583
Doug Anderson37282b42011-03-04 11:54:18 -0800584 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
585 # We don't actually want hooks to define their main with this argument--
586 # it's there to remind them that their hook should always take **kwargs.
587 # For instance, a pre-upload hook should be defined like:
588 # def main(project_list, **kwargs):
589 #
590 # This allows us to later expand the API without breaking old hooks.
591 kwargs = kwargs.copy()
592 kwargs['hook_should_take_kwargs'] = True
593
594 # Call the main function in the hook. If the hook should cause the
595 # build to fail, it will raise an Exception. We'll catch that convert
596 # to a HookError w/ just the failing traceback.
597 try:
598 context['main'](**kwargs)
599 except Exception:
600 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700601 'above.' % (traceback.format_exc(),
602 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800603 finally:
604 # Restore sys.path and CWD.
605 sys.path = orig_syspath
606 os.chdir(orig_path)
607
608 def Run(self, user_allows_all_hooks, **kwargs):
609 """Run the hook.
610
611 If the hook doesn't exist (because there is no hooks project or because
612 this particular hook is not enabled), this is a no-op.
613
614 Args:
615 user_allows_all_hooks: If True, we will never prompt about running the
616 hook--we'll just assume it's OK to run it.
617 kwargs: Keyword arguments to pass to the hook. These are often specific
618 to the hook type. For instance, pre-upload hooks will contain
619 a project_list.
620
621 Raises:
622 HookError: If there was a problem finding the hook or the user declined
623 to run a required hook (from _CheckForHookApproval).
624 """
625 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700626 if ((not self._hooks_project) or (self._hook_type not in
627 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800628 return
629
630 # Bail with a nice error if we can't find the hook.
631 if not os.path.isfile(self._script_fullpath):
632 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
633
634 # Make sure the user is OK with running the hook.
635 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
636 return
637
638 # Run the hook with the same version of python we're using.
639 self._ExecuteHook(**kwargs)
640
641
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600643 # These objects can be shared between several working trees.
644 shareable_files = ['description', 'info']
645 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
646 # These objects can only be used by a single working tree.
647 working_tree_files = ['config', 'packed-refs', 'shallow']
648 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700649
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650 def __init__(self,
651 manifest,
652 name,
653 remote,
654 gitdir,
David James8d201162013-10-11 17:03:19 -0700655 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 worktree,
657 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700658 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800659 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100660 rebase=True,
661 groups=None,
662 sync_c=False,
663 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900664 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100665 clone_depth=None,
666 upstream=None,
667 parent=None,
668 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900669 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700670 optimized_fetch=False,
671 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800672 """Init a Project object.
673
674 Args:
675 manifest: The XmlManifest object.
676 name: The `name` attribute of manifest.xml's project element.
677 remote: RemoteSpec object specifying its remote's properties.
678 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700679 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800680 worktree: Absolute path of git working tree.
681 relpath: Relative path of git working tree to repo's top directory.
682 revisionExpr: The `revision` attribute of manifest.xml's project element.
683 revisionId: git commit id for checking out.
684 rebase: The `rebase` attribute of manifest.xml's project element.
685 groups: The `groups` attribute of manifest.xml's project element.
686 sync_c: The `sync-c` attribute of manifest.xml's project element.
687 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900688 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800689 upstream: The `upstream` attribute of manifest.xml's project element.
690 parent: The parent Project object.
691 is_derived: False if the project was explicitly defined in the manifest;
692 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400693 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900694 optimized_fetch: If True, when a project is set to a sha1 revision, only
695 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700696 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800697 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698 self.manifest = manifest
699 self.name = name
700 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800701 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700702 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800703 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700704 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800705 else:
706 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700708 self.revisionExpr = revisionExpr
709
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700710 if revisionId is None \
711 and revisionExpr \
712 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700713 self.revisionId = revisionExpr
714 else:
715 self.revisionId = revisionId
716
Mike Pontillod3153822012-02-28 11:53:24 -0800717 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700718 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700719 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800720 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900721 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900722 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700723 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 self.parent = parent
725 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900726 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800727 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800728
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700729 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500731 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500732 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700733 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
734 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800736 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700737 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800738 else:
739 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700740 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700741 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700742 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400743 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700744 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745
Doug Anderson37282b42011-03-04 11:54:18 -0800746 # This will be filled in if a project is later identified to be the
747 # project containing repo hooks.
748 self.enabled_repo_hooks = []
749
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700750 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800751 def Derived(self):
752 return self.is_derived
753
754 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700756 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757
758 @property
759 def CurrentBranch(self):
760 """Obtain the name of the currently checked out branch.
761 The branch name omits the 'refs/heads/' prefix.
762 None is returned if the project is on a detached HEAD.
763 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700764 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 if b.startswith(R_HEADS):
766 return b[len(R_HEADS):]
767 return None
768
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700769 def IsRebaseInProgress(self):
770 w = self.worktree
771 g = os.path.join(w, '.git')
772 return os.path.exists(os.path.join(g, 'rebase-apply')) \
773 or os.path.exists(os.path.join(g, 'rebase-merge')) \
774 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200775
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 def IsDirty(self, consider_untracked=True):
777 """Is the working directory modified in some way?
778 """
779 self.work_git.update_index('-q',
780 '--unmerged',
781 '--ignore-missing',
782 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900783 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784 return True
785 if self.work_git.DiffZ('diff-files'):
786 return True
787 if consider_untracked and self.work_git.LsOthers():
788 return True
789 return False
790
791 _userident_name = None
792 _userident_email = None
793
794 @property
795 def UserName(self):
796 """Obtain the user's personal name.
797 """
798 if self._userident_name is None:
799 self._LoadUserIdentity()
800 return self._userident_name
801
802 @property
803 def UserEmail(self):
804 """Obtain the user's email address. This is very likely
805 to be their Gerrit login.
806 """
807 if self._userident_email is None:
808 self._LoadUserIdentity()
809 return self._userident_email
810
811 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900812 u = self.bare_git.var('GIT_COMMITTER_IDENT')
813 m = re.compile("^(.*) <([^>]*)> ").match(u)
814 if m:
815 self._userident_name = m.group(1)
816 self._userident_email = m.group(2)
817 else:
818 self._userident_name = ''
819 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
821 def GetRemote(self, name):
822 """Get the configuration for a single remote.
823 """
824 return self.config.GetRemote(name)
825
826 def GetBranch(self, name):
827 """Get the configuration for a single branch.
828 """
829 return self.config.GetBranch(name)
830
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 def GetBranches(self):
832 """Get all existing local branches.
833 """
834 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900835 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530838 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700839 if name.startswith(R_HEADS):
840 name = name[len(R_HEADS):]
841 b = self.GetBranch(name)
842 b.current = name == current
843 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845 heads[name] = b
846
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530847 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700848 if name.startswith(R_PUB):
849 name = name[len(R_PUB):]
850 b = heads.get(name)
851 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900852 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700853
854 return heads
855
Colin Cross5acde752012-03-28 20:15:45 -0700856 def MatchesGroups(self, manifest_groups):
857 """Returns true if the manifest groups specified at init should cause
858 this project to be synced.
859 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700861
862 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700864 manifest_groups: "-group1,group2"
865 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500866
867 The special manifest group "default" will match any project that
868 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700869 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500870 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700872 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500873 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700874
Conley Owens971de8e2012-04-16 10:36:08 -0700875 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876 for group in expanded_manifest_groups:
877 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700878 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700879 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700880 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700881
Conley Owens971de8e2012-04-16 10:36:08 -0700882 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700884# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 def UncommitedFiles(self, get_all=True):
886 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700887
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700888 Args:
889 get_all: a boolean, if True - get information about all different
890 uncommitted files. If False - return as soon as any kind of
891 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500892 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700893 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894 self.work_git.update_index('-q',
895 '--unmerged',
896 '--ignore-missing',
897 '--refresh')
898 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700899 details.append("rebase in progress")
900 if not get_all:
901 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500902
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700903 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
904 if changes:
905 details.extend(changes)
906 if not get_all:
907 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500908
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700909 changes = self.work_git.DiffZ('diff-files').keys()
910 if changes:
911 details.extend(changes)
912 if not get_all:
913 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500914
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700915 changes = self.work_git.LsOthers()
916 if changes:
917 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500918
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700919 return details
920
921 def HasChanges(self):
922 """Returns true if there are uncommitted changes.
923 """
924 if self.UncommitedFiles(get_all=False):
925 return True
926 else:
927 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500928
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600929 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200931
932 Args:
933 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600934 quiet: If True then only print the project name. Do not print
935 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700937 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700938 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200939 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700940 print(file=output_redir)
941 print('project %s/' % self.relpath, file=output_redir)
942 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700943 return
944
945 self.work_git.update_index('-q',
946 '--unmerged',
947 '--ignore-missing',
948 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700949 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
951 df = self.work_git.DiffZ('diff-files')
952 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100953 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700954 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
956 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700957 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200958 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700959 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700960
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600961 if quiet:
962 out.nl()
963 return 'DIRTY'
964
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700965 branch = self.CurrentBranch
966 if branch is None:
967 out.nobranch('(*** NO BRANCH ***)')
968 else:
969 out.branch('branch %s', branch)
970 out.nl()
971
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700972 if rb:
973 out.important('prior sync failed; rebase still in progress')
974 out.nl()
975
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700976 paths = list()
977 paths.extend(di.keys())
978 paths.extend(df.keys())
979 paths.extend(do)
980
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530981 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900982 try:
983 i = di[p]
984 except KeyError:
985 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900987 try:
988 f = df[p]
989 except KeyError:
990 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200991
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900992 if i:
993 i_status = i.status.upper()
994 else:
995 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700996
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900997 if f:
998 f_status = f.status.lower()
999 else:
1000 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
1002 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001003 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001004 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005 else:
1006 line = ' %s%s\t%s' % (i_status, f_status, p)
1007
1008 if i and not f:
1009 out.added('%s', line)
1010 elif (i and f) or (not i and f):
1011 out.changed('%s', line)
1012 elif not i and not f:
1013 out.untracked('%s', line)
1014 else:
1015 out.write('%s', line)
1016 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001017
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001018 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001019
pelyad67872d2012-03-28 14:49:58 +03001020 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 """Prints the status of the repository to stdout.
1022 """
1023 out = DiffColoring(self.config)
1024 cmd = ['diff']
1025 if out.is_on:
1026 cmd.append('--color')
1027 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001028 if absolute_paths:
1029 cmd.append('--src-prefix=a/%s/' % self.relpath)
1030 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031 cmd.append('--')
1032 p = GitCommand(self,
1033 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001034 capture_stdout=True,
1035 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 has_diff = False
1037 for line in p.process.stdout:
1038 if not has_diff:
1039 out.nl()
1040 out.project('project %s/' % self.relpath)
1041 out.nl()
1042 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001043 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044 p.Wait()
1045
1046
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001047# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001048
David Pursehouse8a68ff92012-09-24 12:15:13 +09001049 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 """Was the branch published (uploaded) for code review?
1051 If so, returns the SHA-1 hash of the last published
1052 state for the branch.
1053 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001054 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001055 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001056 try:
1057 return self.bare_git.rev_parse(key)
1058 except GitError:
1059 return None
1060 else:
1061 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001062 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001063 except KeyError:
1064 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 """Prunes any stale published refs.
1068 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 if all_refs is None:
1070 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 heads = set()
1072 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301073 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 if name.startswith(R_HEADS):
1075 heads.add(name)
1076 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001077 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301079 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080 n = name[len(R_PUB):]
1081 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001082 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001084 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 """List any branches which can be uploaded for review.
1086 """
1087 heads = {}
1088 pubed = {}
1089
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301090 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001092 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001094 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095
1096 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301097 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001098 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001099 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001100 if selected_branch and branch != selected_branch:
1101 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001103 rb = self.GetUploadableBranch(branch)
1104 if rb:
1105 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001106 return ready
1107
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001108 def GetUploadableBranch(self, branch_name):
1109 """Get a single uploadable branch, or None.
1110 """
1111 branch = self.GetBranch(branch_name)
1112 base = branch.LocalMerge
1113 if branch.LocalMerge:
1114 rb = ReviewableBranch(self, branch, base)
1115 if rb.commits:
1116 return rb
1117 return None
1118
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001119 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001120 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001121 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001122 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001123 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001124 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001125 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001126 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001127 validate_certs=True,
1128 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001129 """Uploads the named branch for code review.
1130 """
1131 if branch is None:
1132 branch = self.CurrentBranch
1133 if branch is None:
1134 raise GitError('not currently on a branch')
1135
1136 branch = self.GetBranch(branch)
1137 if not branch.LocalMerge:
1138 raise GitError('branch %s does not track a remote' % branch.name)
1139 if not branch.remote.review:
1140 raise GitError('remote %s has no review url' % branch.remote.name)
1141
Bryan Jacobsf609f912013-05-06 13:36:24 -04001142 if dest_branch is None:
1143 dest_branch = self.dest_branch
1144 if dest_branch is None:
1145 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001146 if not dest_branch.startswith(R_HEADS):
1147 dest_branch = R_HEADS + dest_branch
1148
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001149 if not branch.remote.projectname:
1150 branch.remote.projectname = self.name
1151 branch.remote.Save()
1152
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001153 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001154 if url is None:
1155 raise UploadError('review not configured')
1156 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001157
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001158 if url.startswith('ssh://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001159 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001160
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001161 for push_option in (push_options or []):
1162 cmd.append('-o')
1163 cmd.append(push_option)
1164
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001165 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001166
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 if dest_branch.startswith(R_HEADS):
1168 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001169
1170 upload_type = 'for'
1171 if draft:
1172 upload_type = 'drafts'
1173
1174 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1175 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001176 if auto_topic:
1177 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001178
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001179 opts = ['r=%s' % p for p in people[0]]
1180 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001181 if notify:
1182 opts += ['notify=' + notify]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001183 if private:
1184 opts += ['private']
1185 if wip:
1186 opts += ['wip']
1187 if opts:
1188 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001189 cmd.append(ref_spec)
1190
Anthony King7bdac712014-07-16 12:56:40 +01001191 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001192 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001193
1194 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1195 self.bare_git.UpdateRef(R_PUB + branch.name,
1196 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001197 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001198
1199
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001200# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
Julien Campergue335f5ef2013-10-16 11:02:35 +02001202 def _ExtractArchive(self, tarpath, path=None):
1203 """Extract the given tar on its current location
1204
1205 Args:
1206 - tarpath: The path to the actual tar file
1207
1208 """
1209 try:
1210 with tarfile.open(tarpath, 'r') as tar:
1211 tar.extractall(path=path)
1212 return True
1213 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001214 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001215 return False
1216
Ningning Xiac2fbc782016-08-22 14:24:39 -07001217 def CachePopulate(self, cache_dir, url):
1218 """Populate cache in the cache_dir.
1219
1220 Args:
1221 cache_dir: Directory to cache git files from Google Storage.
1222 url: Git url of current repository.
1223
1224 Raises:
1225 CacheApplyError if it fails to populate the git cache.
1226 """
1227 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1228 '--cache-dir', cache_dir, url]
1229
1230 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1231 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1232 'url: %s' % (cache_dir, url))
1233
1234 def CacheExists(self, cache_dir, url):
1235 """Check the existence of the cache files.
1236
1237 Args:
1238 cache_dir: Directory to cache git files.
1239 url: Git url of current repository.
1240
1241 Raises:
1242 CacheApplyError if the cache files do not exist.
1243 """
1244 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1245
1246 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1247 if exist.Wait() != 0:
1248 raise CacheApplyError('Failed to execute git cache exists cmd. '
1249 'cache_dir: %s url: %s' % (cache_dir, url))
1250
1251 if not exist.stdout or not exist.stdout.strip():
1252 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1253 'url: %s' % (cache_dir, url))
1254 return exist.stdout.strip()
1255
1256 def CacheApply(self, cache_dir):
1257 """Apply git cache files populated from Google Storage buckets.
1258
1259 Args:
1260 cache_dir: Directory to cache git files.
1261
1262 Raises:
1263 CacheApplyError if it fails to apply git caches.
1264 """
1265 remote = self.GetRemote(self.remote.name)
1266
1267 self.CachePopulate(cache_dir, remote.url)
1268
1269 mirror_dir = self.CacheExists(cache_dir, remote.url)
1270
1271 refspec = RefSpec(True, 'refs/heads/*',
1272 'refs/remotes/%s/*' % remote.name)
1273
1274 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1275 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1276 raise CacheApplyError('Failed to fetch refs %s from %s' %
1277 (mirror_dir, str(refspec)))
1278
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001279 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001280 quiet=False,
1281 is_new=None,
1282 current_branch_only=False,
1283 force_sync=False,
1284 clone_bundle=True,
1285 no_tags=False,
1286 archive=False,
1287 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001288 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001289 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001290 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291 """Perform only the network IO portion of the sync process.
1292 Local working directory/branch state is not affected.
1293 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001294 if archive and not isinstance(self, MetaProject):
1295 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001296 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001297 return False
1298
1299 name = self.relpath.replace('\\', '/')
1300 name = name.replace('/', '_')
1301 tarpath = '%s.tar' % name
1302 topdir = self.manifest.topdir
1303
1304 try:
1305 self._FetchArchive(tarpath, cwd=topdir)
1306 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001307 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001308 return False
1309
1310 # From now on, we only need absolute tarpath
1311 tarpath = os.path.join(topdir, tarpath)
1312
1313 if not self._ExtractArchive(tarpath, path=topdir):
1314 return False
1315 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001316 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001317 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001318 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001319 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001320 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001321 if is_new is None:
1322 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001323 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001324 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001325 else:
1326 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001327 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001328
1329 if is_new:
1330 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1331 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001332 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001333 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001334 # This works for both absolute and relative alternate directories.
1335 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001336 finally:
1337 fd.close()
1338 except IOError:
1339 alt_dir = None
1340 else:
1341 alt_dir = None
1342
Ningning Xiac2fbc782016-08-22 14:24:39 -07001343 applied_cache = False
1344 # If cache_dir is provided, and it's a new repository without
1345 # alternative_dir, bootstrap this project repo with the git
1346 # cache files.
1347 if cache_dir is not None and is_new and alt_dir is None:
1348 try:
1349 self.CacheApply(cache_dir)
1350 applied_cache = True
1351 is_new = False
1352 except CacheApplyError as e:
1353 _error('Could not apply git cache: %s', e)
1354 _error('Please check if you have the right GS credentials.')
1355 _error('Please check if the cache files exist in GS.')
1356
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001357 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001358 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001359 and alt_dir is None \
1360 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001361 is_new = False
1362
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001363 if not current_branch_only:
1364 if self.sync_c:
1365 current_branch_only = True
1366 elif not self.manifest._loaded:
1367 # Manifest cannot check defaults until it syncs.
1368 current_branch_only = False
1369 elif self.manifest.default.sync_c:
1370 current_branch_only = True
1371
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001372 if not no_tags:
1373 if not self.sync_tags:
1374 no_tags = True
1375
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001376 if self.clone_depth:
1377 depth = self.clone_depth
1378 else:
1379 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1380
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001381 need_to_fetch = not (optimized_fetch and
1382 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001383 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001384 if (need_to_fetch and
1385 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1386 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001387 no_tags=no_tags, prune=prune, depth=depth,
1388 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001389 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001390
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001391 mp = self.manifest.manifestProject
1392 dissociate = mp.config.GetBoolean('repo.dissociate')
1393 if dissociate:
1394 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1395 if os.path.exists(alternates_file):
1396 cmd = ['repack', '-a', '-d']
1397 if GitCommand(self, cmd, bare=True).Wait() != 0:
1398 return False
1399 platform_utils.remove(alternates_file)
1400
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001401 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001402 self._InitMRef()
1403 else:
1404 self._InitMirrorHead()
1405 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001406 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001407 except OSError:
1408 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001409 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001410
1411 def PostRepoUpgrade(self):
1412 self._InitHooks()
1413
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001414 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001415 if self.manifest.isGitcClient:
1416 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001417 for copyfile in self.copyfiles:
1418 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001419 for linkfile in self.linkfiles:
1420 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001421
Julien Camperguedd654222014-01-09 16:21:37 +01001422 def GetCommitRevisionId(self):
1423 """Get revisionId of a commit.
1424
1425 Use this method instead of GetRevisionId to get the id of the commit rather
1426 than the id of the current git object (for example, a tag)
1427
1428 """
1429 if not self.revisionExpr.startswith(R_TAGS):
1430 return self.GetRevisionId(self._allrefs)
1431
1432 try:
1433 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1434 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001435 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1436 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001437
David Pursehouse8a68ff92012-09-24 12:15:13 +09001438 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001439 if self.revisionId:
1440 return self.revisionId
1441
1442 rem = self.GetRemote(self.remote.name)
1443 rev = rem.ToLocal(self.revisionExpr)
1444
David Pursehouse8a68ff92012-09-24 12:15:13 +09001445 if all_refs is not None and rev in all_refs:
1446 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001447
1448 try:
1449 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1450 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001451 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1452 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001453
Martin Kellye4e94d22017-03-21 16:05:12 -07001454 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 """Perform only the local IO portion of the sync process.
1456 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001457 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001458 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001459 all_refs = self.bare_ref.all
1460 self.CleanPublishedCache(all_refs)
1461 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001462
David Pursehouse1d947b32012-10-25 12:23:11 +09001463 def _doff():
1464 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001465 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001466
Martin Kellye4e94d22017-03-21 16:05:12 -07001467 def _dosubmodules():
1468 self._SyncSubmodules(quiet=True)
1469
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001470 head = self.work_git.GetHead()
1471 if head.startswith(R_HEADS):
1472 branch = head[len(R_HEADS):]
1473 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001474 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001475 except KeyError:
1476 head = None
1477 else:
1478 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001479
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001480 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001481 # Currently on a detached HEAD. The user is assumed to
1482 # not have any local modifications worth worrying about.
1483 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001484 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001485 syncbuf.fail(self, _PriorSyncFailedError())
1486 return
1487
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001488 if head == revid:
1489 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001490 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001491 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001492 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001493 # The copy/linkfile config may have changed.
1494 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001495 return
1496 else:
1497 lost = self._revlist(not_rev(revid), HEAD)
1498 if lost:
1499 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001500
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001501 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001502 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001503 if submodules:
1504 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001505 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001506 syncbuf.fail(self, e)
1507 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001508 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001509 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001510
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001511 if head == revid:
1512 # No changes; don't do anything further.
1513 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001514 # The copy/linkfile config may have changed.
1515 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001516 return
1517
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001518 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001520 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001521 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001522 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001524 syncbuf.info(self,
1525 "leaving %s; does not track upstream",
1526 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001527 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001528 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001529 if submodules:
1530 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001531 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001532 syncbuf.fail(self, e)
1533 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001534 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001535 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001536
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001537 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001538 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001539 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001540 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541 if not_merged:
1542 if upstream_gain:
1543 # The user has published this branch and some of those
1544 # commits are not yet merged upstream. We do not want
1545 # to rewrite the published commits so we punt.
1546 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001547 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001548 "branch %s is published (but not merged) and is now "
1549 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001550 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001551 elif pub == head:
1552 # All published commits are merged, and thus we are a
1553 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001554 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001555 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001556 if submodules:
1557 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001558 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001560 # Examine the local commits not in the remote. Find the
1561 # last one attributed to this user, if any.
1562 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001563 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001564 last_mine = None
1565 cnt_mine = 0
1566 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301567 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001568 if committer_email == self.UserEmail:
1569 last_mine = commit_id
1570 cnt_mine += 1
1571
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001572 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001573 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001574
1575 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001576 syncbuf.fail(self, _DirtyError())
1577 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001578
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001579 # If the upstream switched on us, warn the user.
1580 #
1581 if branch.merge != self.revisionExpr:
1582 if branch.merge and self.revisionExpr:
1583 syncbuf.info(self,
1584 'manifest switched %s...%s',
1585 branch.merge,
1586 self.revisionExpr)
1587 elif branch.merge:
1588 syncbuf.info(self,
1589 'manifest no longer tracks %s',
1590 branch.merge)
1591
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001592 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001593 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001594 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001595 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001596 syncbuf.info(self,
1597 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001598 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001599
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001600 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001601 if not ID_RE.match(self.revisionExpr):
1602 # in case of manifest sync the revisionExpr might be a SHA1
1603 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001604 if not branch.merge.startswith('refs/'):
1605 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001606 branch.Save()
1607
Mike Pontillod3153822012-02-28 11:53:24 -08001608 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001609 def _docopyandlink():
1610 self._CopyAndLinkFiles()
1611
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001612 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001613 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001614 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001615 if submodules:
1616 syncbuf.later2(self, _dosubmodules)
1617 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001618 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001619 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001620 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001621 if submodules:
1622 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001623 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001624 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001625 syncbuf.fail(self, e)
1626 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001628 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001629 if submodules:
1630 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001631
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001632 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001633 # dest should already be an absolute path, but src is project relative
1634 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001635 abssrc = os.path.join(self.worktree, src)
1636 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001637
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001638 def AddLinkFile(self, src, dest, absdest):
1639 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001640 # make src relative path to dest
1641 absdestdir = os.path.dirname(absdest)
1642 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001643 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001644
James W. Mills24c13082012-04-12 15:04:13 -05001645 def AddAnnotation(self, name, value, keep):
1646 self.annotations.append(_Annotation(name, value, keep))
1647
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001648 def DownloadPatchSet(self, change_id, patch_id):
1649 """Download a single patch set of a single change to FETCH_HEAD.
1650 """
1651 remote = self.GetRemote(self.remote.name)
1652
1653 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001654 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001655 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001656 if GitCommand(self, cmd, bare=True).Wait() != 0:
1657 return None
1658 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001659 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001660 change_id,
1661 patch_id,
1662 self.bare_git.rev_parse('FETCH_HEAD'))
1663
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001664
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001665# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001666
Simran Basib9a1b732015-08-20 12:19:28 -07001667 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001668 """Create a new branch off the manifest's revision.
1669 """
Simran Basib9a1b732015-08-20 12:19:28 -07001670 if not branch_merge:
1671 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001672 head = self.work_git.GetHead()
1673 if head == (R_HEADS + name):
1674 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001675
David Pursehouse8a68ff92012-09-24 12:15:13 +09001676 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001677 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001678 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001679 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001680 capture_stdout=True,
1681 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001682
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001683 branch = self.GetBranch(name)
1684 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001685 branch.merge = branch_merge
1686 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1687 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001688 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001689
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001690 if head.startswith(R_HEADS):
1691 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001692 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001693 except KeyError:
1694 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001695 if revid and head and revid == head:
1696 ref = os.path.join(self.gitdir, R_HEADS + name)
1697 try:
1698 os.makedirs(os.path.dirname(ref))
1699 except OSError:
1700 pass
1701 _lwrite(ref, '%s\n' % revid)
1702 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1703 'ref: %s%s\n' % (R_HEADS, name))
1704 branch.Save()
1705 return True
1706
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001707 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001708 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001709 capture_stdout=True,
1710 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001711 branch.Save()
1712 return True
1713 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714
Wink Saville02d79452009-04-10 13:01:24 -07001715 def CheckoutBranch(self, name):
1716 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001717
1718 Args:
1719 name: The name of the branch to checkout.
1720
1721 Returns:
1722 True if the checkout succeeded; False if it didn't; None if the branch
1723 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001724 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001725 rev = R_HEADS + name
1726 head = self.work_git.GetHead()
1727 if head == rev:
1728 # Already on the branch
1729 #
1730 return True
Wink Saville02d79452009-04-10 13:01:24 -07001731
David Pursehouse8a68ff92012-09-24 12:15:13 +09001732 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001733 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001734 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001735 except KeyError:
1736 # Branch does not exist in this project
1737 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001738 return None
Wink Saville02d79452009-04-10 13:01:24 -07001739
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001740 if head.startswith(R_HEADS):
1741 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001742 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001743 except KeyError:
1744 head = None
1745
1746 if head == revid:
1747 # Same revision; just update HEAD to point to the new
1748 # target branch, but otherwise take no other action.
1749 #
1750 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1751 'ref: %s%s\n' % (R_HEADS, name))
1752 return True
1753
1754 return GitCommand(self,
1755 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001756 capture_stdout=True,
1757 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001758
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001759 def AbandonBranch(self, name):
1760 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001761
1762 Args:
1763 name: The name of the branch to abandon.
1764
1765 Returns:
1766 True if the abandon succeeded; False if it didn't; None if the branch
1767 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001768 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001769 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001770 all_refs = self.bare_ref.all
1771 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001772 # Doesn't exist
1773 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001774
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001775 head = self.work_git.GetHead()
1776 if head == rev:
1777 # We can't destroy the branch while we are sitting
1778 # on it. Switch to a detached HEAD.
1779 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001780 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001781
David Pursehouse8a68ff92012-09-24 12:15:13 +09001782 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001783 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001784 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1785 '%s\n' % revid)
1786 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001787 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001788
1789 return GitCommand(self,
1790 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001791 capture_stdout=True,
1792 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001793
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001794 def PruneHeads(self):
1795 """Prune any topic branches already merged into upstream.
1796 """
1797 cb = self.CurrentBranch
1798 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001799 left = self._allrefs
1800 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001801 if name.startswith(R_HEADS):
1802 name = name[len(R_HEADS):]
1803 if cb is None or name != cb:
1804 kill.append(name)
1805
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001806 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001807 if cb is not None \
1808 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001809 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001810 self.work_git.DetachHead(HEAD)
1811 kill.append(cb)
1812
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001813 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001814 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001815
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001816 try:
1817 self.bare_git.DetachHead(rev)
1818
1819 b = ['branch', '-d']
1820 b.extend(kill)
1821 b = GitCommand(self, b, bare=True,
1822 capture_stdout=True,
1823 capture_stderr=True)
1824 b.Wait()
1825 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001826 if ID_RE.match(old):
1827 self.bare_git.DetachHead(old)
1828 else:
1829 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001830 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001831
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001832 for branch in kill:
1833 if (R_HEADS + branch) not in left:
1834 self.CleanPublishedCache()
1835 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001836
1837 if cb and cb not in kill:
1838 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001839 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001840
1841 kept = []
1842 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001843 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001844 branch = self.GetBranch(branch)
1845 base = branch.LocalMerge
1846 if not base:
1847 base = rev
1848 kept.append(ReviewableBranch(self, branch, base))
1849 return kept
1850
1851
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001852# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001853
1854 def GetRegisteredSubprojects(self):
1855 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001856
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001857 def rec(subprojects):
1858 if not subprojects:
1859 return
1860 result.extend(subprojects)
1861 for p in subprojects:
1862 rec(p.subprojects)
1863 rec(self.subprojects)
1864 return result
1865
1866 def _GetSubmodules(self):
1867 # Unfortunately we cannot call `git submodule status --recursive` here
1868 # because the working tree might not exist yet, and it cannot be used
1869 # without a working tree in its current implementation.
1870
1871 def get_submodules(gitdir, rev):
1872 # Parse .gitmodules for submodule sub_paths and sub_urls
1873 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1874 if not sub_paths:
1875 return []
1876 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1877 # revision of submodule repository
1878 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1879 submodules = []
1880 for sub_path, sub_url in zip(sub_paths, sub_urls):
1881 try:
1882 sub_rev = sub_revs[sub_path]
1883 except KeyError:
1884 # Ignore non-exist submodules
1885 continue
1886 submodules.append((sub_rev, sub_path, sub_url))
1887 return submodules
1888
1889 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1890 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001891
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001892 def parse_gitmodules(gitdir, rev):
1893 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1894 try:
Anthony King7bdac712014-07-16 12:56:40 +01001895 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1896 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001897 except GitError:
1898 return [], []
1899 if p.Wait() != 0:
1900 return [], []
1901
1902 gitmodules_lines = []
1903 fd, temp_gitmodules_path = tempfile.mkstemp()
1904 try:
1905 os.write(fd, p.stdout)
1906 os.close(fd)
1907 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001908 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1909 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001910 if p.Wait() != 0:
1911 return [], []
1912 gitmodules_lines = p.stdout.split('\n')
1913 except GitError:
1914 return [], []
1915 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001916 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001917
1918 names = set()
1919 paths = {}
1920 urls = {}
1921 for line in gitmodules_lines:
1922 if not line:
1923 continue
1924 m = re_path.match(line)
1925 if m:
1926 names.add(m.group(1))
1927 paths[m.group(1)] = m.group(2)
1928 continue
1929 m = re_url.match(line)
1930 if m:
1931 names.add(m.group(1))
1932 urls[m.group(1)] = m.group(2)
1933 continue
1934 names = sorted(names)
1935 return ([paths.get(name, '') for name in names],
1936 [urls.get(name, '') for name in names])
1937
1938 def git_ls_tree(gitdir, rev, paths):
1939 cmd = ['ls-tree', rev, '--']
1940 cmd.extend(paths)
1941 try:
Anthony King7bdac712014-07-16 12:56:40 +01001942 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1943 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001944 except GitError:
1945 return []
1946 if p.Wait() != 0:
1947 return []
1948 objects = {}
1949 for line in p.stdout.split('\n'):
1950 if not line.strip():
1951 continue
1952 object_rev, object_path = line.split()[2:4]
1953 objects[object_path] = object_rev
1954 return objects
1955
1956 try:
1957 rev = self.GetRevisionId()
1958 except GitError:
1959 return []
1960 return get_submodules(self.gitdir, rev)
1961
1962 def GetDerivedSubprojects(self):
1963 result = []
1964 if not self.Exists:
1965 # If git repo does not exist yet, querying its submodules will
1966 # mess up its states; so return here.
1967 return result
1968 for rev, path, url in self._GetSubmodules():
1969 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001970 relpath, worktree, gitdir, objdir = \
1971 self.manifest.GetSubprojectPaths(self, name, path)
1972 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001973 if project:
1974 result.extend(project.GetDerivedSubprojects())
1975 continue
David James8d201162013-10-11 17:03:19 -07001976
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001977 if url.startswith('..'):
1978 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001979 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001980 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001981 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001982 review=self.remote.review,
1983 revision=self.remote.revision)
1984 subproject = Project(manifest=self.manifest,
1985 name=name,
1986 remote=remote,
1987 gitdir=gitdir,
1988 objdir=objdir,
1989 worktree=worktree,
1990 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001991 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001992 revisionId=rev,
1993 rebase=self.rebase,
1994 groups=self.groups,
1995 sync_c=self.sync_c,
1996 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001997 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001998 parent=self,
1999 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002000 result.append(subproject)
2001 result.extend(subproject.GetDerivedSubprojects())
2002 return result
2003
2004
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002005# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002006 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002007 try:
2008 # if revision (sha or tag) is not present then following function
2009 # throws an error.
2010 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2011 return True
2012 except GitError:
2013 # There is no such persistent revision. We have to fetch it.
2014 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002015
Julien Campergue335f5ef2013-10-16 11:02:35 +02002016 def _FetchArchive(self, tarpath, cwd=None):
2017 cmd = ['archive', '-v', '-o', tarpath]
2018 cmd.append('--remote=%s' % self.remote.url)
2019 cmd.append('--prefix=%s/' % self.relpath)
2020 cmd.append(self.revisionExpr)
2021
2022 command = GitCommand(self, cmd, cwd=cwd,
2023 capture_stdout=True,
2024 capture_stderr=True)
2025
2026 if command.Wait() != 0:
2027 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2028
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002029 def _RemoteFetch(self, name=None,
2030 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002031 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002032 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002033 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002034 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002035 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002036 depth=None,
2037 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002038
2039 is_sha1 = False
2040 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002041 # The depth should not be used when fetching to a mirror because
2042 # it will result in a shallow repository that cannot be cloned or
2043 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002044 # The repo project should also never be synced with partial depth.
2045 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2046 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002047
Shawn Pearce69e04d82014-01-29 12:48:54 -08002048 if depth:
2049 current_branch_only = True
2050
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002051 if ID_RE.match(self.revisionExpr) is not None:
2052 is_sha1 = True
2053
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002054 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002055 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002056 # this is a tag and its sha1 value should never change
2057 tag_name = self.revisionExpr[len(R_TAGS):]
2058
2059 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002060 if self._CheckForImmutableRevision():
2061 print('Skipped fetching project %s (already have persistent ref)'
2062 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002063 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002064 if is_sha1 and not depth:
2065 # When syncing a specific commit and --depth is not set:
2066 # * if upstream is explicitly specified and is not a sha1, fetch only
2067 # upstream as users expect only upstream to be fetch.
2068 # Note: The commit might not be in upstream in which case the sync
2069 # will fail.
2070 # * otherwise, fetch all branches to make sure we end up with the
2071 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002072 if self.upstream:
2073 current_branch_only = not ID_RE.match(self.upstream)
2074 else:
2075 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002076
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002077 if not name:
2078 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002079
2080 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002081 remote = self.GetRemote(name)
2082 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002083 ssh_proxy = True
2084
Shawn O. Pearce88443382010-10-08 10:02:09 +02002085 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002086 if alt_dir and 'objects' == os.path.basename(alt_dir):
2087 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002088 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2089 remote = self.GetRemote(name)
2090
David Pursehouse8a68ff92012-09-24 12:15:13 +09002091 all_refs = self.bare_ref.all
2092 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002093 tmp = set()
2094
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302095 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002096 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002097 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002098 all_refs[r] = ref_id
2099 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 continue
2101
David Pursehouse8a68ff92012-09-24 12:15:13 +09002102 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002103 continue
2104
David Pursehouse8a68ff92012-09-24 12:15:13 +09002105 r = 'refs/_alt/%s' % ref_id
2106 all_refs[r] = ref_id
2107 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002108 tmp.add(r)
2109
heping3d7bbc92017-04-12 19:51:47 +08002110 tmp_packed_lines = []
2111 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002112
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302113 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002114 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002115 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002116 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002117 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002118
heping3d7bbc92017-04-12 19:51:47 +08002119 tmp_packed = ''.join(tmp_packed_lines)
2120 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002122 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002123 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002124
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002125 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002126
Conley Owensf97e8382015-01-21 11:12:46 -08002127 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002128 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002129 else:
2130 # If this repo has shallow objects, then we don't know which refs have
2131 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2132 # do this with projects that don't have shallow objects, since it is less
2133 # efficient.
2134 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2135 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002136
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002137 if quiet:
2138 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002139 if not self.worktree:
2140 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002141 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002142
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002143 # If using depth then we should not get all the tags since they may
2144 # be outside of the depth.
2145 if no_tags or depth:
2146 cmd.append('--no-tags')
2147 else:
2148 cmd.append('--tags')
2149
David Pursehouse74cfd272015-10-14 10:50:15 +09002150 if prune:
2151 cmd.append('--prune')
2152
Martin Kellye4e94d22017-03-21 16:05:12 -07002153 if submodules:
2154 cmd.append('--recurse-submodules=on-demand')
2155
Conley Owens80b87fe2014-05-09 17:13:44 -07002156 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002157 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002158 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002159 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002160 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002161 spec.append('tag')
2162 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002163
David Pursehouse403b64e2015-04-27 10:41:33 +09002164 if not self.manifest.IsMirror:
2165 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002166 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002167 # Shallow checkout of a specific commit, fetch from that commit and not
2168 # the heads only as the commit might be deeper in the history.
2169 spec.append(branch)
2170 else:
2171 if is_sha1:
2172 branch = self.upstream
2173 if branch is not None and branch.strip():
2174 if not branch.startswith('refs/'):
2175 branch = R_HEADS + branch
2176 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002177 cmd.extend(spec)
2178
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002179 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002180 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002181 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002182 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002183 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002184 ok = True
2185 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002186 # If needed, run the 'git remote prune' the first time through the loop
2187 elif (not _i and
2188 "error:" in gitcmd.stderr and
2189 "git remote prune" in gitcmd.stderr):
2190 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002191 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002192 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002193 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002194 break
2195 continue
Brian Harring14a66742012-09-28 20:21:57 -07002196 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002197 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2198 # in sha1 mode, we just tried sync'ing from the upstream field; it
2199 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002200 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002201 elif ret < 0:
2202 # Git died with a signal, exit immediately
2203 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002204 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002205
2206 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002207 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002208 if old_packed != '':
2209 _lwrite(packed_refs, old_packed)
2210 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002211 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002212 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002213
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002214 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002215 # We just synced the upstream given branch; verify we
2216 # got what we wanted, else trigger a second run of all
2217 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002218 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002219 if current_branch_only and depth:
2220 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002221 return self._RemoteFetch(name=name,
2222 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002223 initial=False, quiet=quiet, alt_dir=alt_dir,
2224 depth=None)
2225 else:
2226 # Avoid infinite recursion: sync all branches with depth set to None
2227 return self._RemoteFetch(name=name, current_branch_only=False,
2228 initial=False, quiet=quiet, alt_dir=alt_dir,
2229 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002230
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002231 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002232
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002233 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002234 if initial and \
2235 (self.manifest.manifestProject.config.GetString('repo.depth') or
2236 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002237 return False
2238
2239 remote = self.GetRemote(self.remote.name)
2240 bundle_url = remote.url + '/clone.bundle'
2241 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002242 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2243 'persistent-http',
2244 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002245 return False
2246
2247 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2248 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2249
2250 exist_dst = os.path.exists(bundle_dst)
2251 exist_tmp = os.path.exists(bundle_tmp)
2252
2253 if not initial and not exist_dst and not exist_tmp:
2254 return False
2255
2256 if not exist_dst:
2257 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2258 if not exist_dst:
2259 return False
2260
2261 cmd = ['fetch']
2262 if quiet:
2263 cmd.append('--quiet')
2264 if not self.worktree:
2265 cmd.append('--update-head-ok')
2266 cmd.append(bundle_dst)
2267 for f in remote.fetch:
2268 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002269 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002270
2271 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002272 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002273 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002274 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002275 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002276 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002277
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002278 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002279 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002280 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002281
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002282 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002283 if quiet:
2284 cmd += ['--silent']
2285 if os.path.exists(tmpPath):
2286 size = os.stat(tmpPath).st_size
2287 if size >= 1024:
2288 cmd += ['--continue-at', '%d' % (size,)]
2289 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002290 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002291 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2292 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002293 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002294 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002295 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002296 if srcUrl.startswith('persistent-'):
2297 srcUrl = srcUrl[len('persistent-'):]
2298 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002299
Dave Borowitz137d0132015-01-02 11:12:54 -08002300 if IsTrace():
2301 Trace('%s', ' '.join(cmd))
2302 try:
2303 proc = subprocess.Popen(cmd)
2304 except OSError:
2305 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002306
Dave Borowitz137d0132015-01-02 11:12:54 -08002307 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002308
Dave Borowitz137d0132015-01-02 11:12:54 -08002309 if curlret == 22:
2310 # From curl man page:
2311 # 22: HTTP page not retrieved. The requested url was not found or
2312 # returned another error with the HTTP error code being 400 or above.
2313 # This return code only appears if -f, --fail is used.
2314 if not quiet:
2315 print("Server does not provide clone.bundle; ignoring.",
2316 file=sys.stderr)
2317 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002318
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002319 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002320 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002321 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002322 return True
2323 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002324 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002325 return False
2326 else:
2327 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002328
Kris Giesingc8d882a2014-12-23 13:02:32 -08002329 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002330 try:
2331 with open(path) as f:
2332 if f.read(16) == '# v2 git bundle\n':
2333 return True
2334 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002335 if not quiet:
2336 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002337 return False
2338 except OSError:
2339 return False
2340
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002341 def _Checkout(self, rev, quiet=False):
2342 cmd = ['checkout']
2343 if quiet:
2344 cmd.append('-q')
2345 cmd.append(rev)
2346 cmd.append('--')
2347 if GitCommand(self, cmd).Wait() != 0:
2348 if self._allrefs:
2349 raise GitError('%s checkout %s ' % (self.name, rev))
2350
Anthony King7bdac712014-07-16 12:56:40 +01002351 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002352 cmd = ['cherry-pick']
2353 cmd.append(rev)
2354 cmd.append('--')
2355 if GitCommand(self, cmd).Wait() != 0:
2356 if self._allrefs:
2357 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2358
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302359 def _LsRemote(self, refs):
2360 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302361 p = GitCommand(self, cmd, capture_stdout=True)
2362 if p.Wait() == 0:
2363 if hasattr(p.stdout, 'decode'):
2364 return p.stdout.decode('utf-8')
2365 else:
2366 return p.stdout
2367 return None
2368
Anthony King7bdac712014-07-16 12:56:40 +01002369 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002370 cmd = ['revert']
2371 cmd.append('--no-edit')
2372 cmd.append(rev)
2373 cmd.append('--')
2374 if GitCommand(self, cmd).Wait() != 0:
2375 if self._allrefs:
2376 raise GitError('%s revert %s ' % (self.name, rev))
2377
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002378 def _ResetHard(self, rev, quiet=True):
2379 cmd = ['reset', '--hard']
2380 if quiet:
2381 cmd.append('-q')
2382 cmd.append(rev)
2383 if GitCommand(self, cmd).Wait() != 0:
2384 raise GitError('%s reset --hard %s ' % (self.name, rev))
2385
Martin Kellye4e94d22017-03-21 16:05:12 -07002386 def _SyncSubmodules(self, quiet=True):
2387 cmd = ['submodule', 'update', '--init', '--recursive']
2388 if quiet:
2389 cmd.append('-q')
2390 if GitCommand(self, cmd).Wait() != 0:
2391 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2392
Anthony King7bdac712014-07-16 12:56:40 +01002393 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002394 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002395 if onto is not None:
2396 cmd.extend(['--onto', onto])
2397 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002398 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002399 raise GitError('%s rebase %s ' % (self.name, upstream))
2400
Pierre Tardy3d125942012-05-04 12:18:12 +02002401 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002402 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002403 if ffonly:
2404 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002405 if GitCommand(self, cmd).Wait() != 0:
2406 raise GitError('%s merge %s ' % (self.name, head))
2407
Kevin Degiabaa7f32014-11-12 11:27:45 -07002408 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002409 init_git_dir = not os.path.exists(self.gitdir)
2410 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002411 try:
2412 # Initialize the bare repository, which contains all of the objects.
2413 if init_obj_dir:
2414 os.makedirs(self.objdir)
2415 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002416
Kevin Degib1a07b82015-07-27 13:33:43 -06002417 # If we have a separate directory to hold refs, initialize it as well.
2418 if self.objdir != self.gitdir:
2419 if init_git_dir:
2420 os.makedirs(self.gitdir)
2421
2422 if init_obj_dir or init_git_dir:
2423 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2424 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002425 try:
2426 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2427 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002428 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002429 print("Retrying clone after deleting %s" %
2430 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002431 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002432 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2433 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002434 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002435 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002436 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2437 except:
2438 raise e
2439 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002440
Kevin Degi384b3c52014-10-16 16:02:58 -06002441 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002442 mp = self.manifest.manifestProject
2443 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002444
Kevin Degib1a07b82015-07-27 13:33:43 -06002445 if ref_dir or mirror_git:
2446 if not mirror_git:
2447 mirror_git = os.path.join(ref_dir, self.name + '.git')
2448 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2449 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002450
Kevin Degib1a07b82015-07-27 13:33:43 -06002451 if os.path.exists(mirror_git):
2452 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002453
Kevin Degib1a07b82015-07-27 13:33:43 -06002454 elif os.path.exists(repo_git):
2455 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002456
Kevin Degib1a07b82015-07-27 13:33:43 -06002457 else:
2458 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002459
Kevin Degib1a07b82015-07-27 13:33:43 -06002460 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002461 if not os.path.isabs(ref_dir):
2462 # The alternate directory is relative to the object database.
2463 ref_dir = os.path.relpath(ref_dir,
2464 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002465 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2466 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002467
Kevin Degib1a07b82015-07-27 13:33:43 -06002468 self._UpdateHooks()
2469
2470 m = self.manifest.manifestProject.config
2471 for key in ['user.name', 'user.email']:
2472 if m.Has(key, include_defaults=False):
2473 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002474 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002475 if self.manifest.IsMirror:
2476 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002477 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002478 self.config.SetString('core.bare', None)
2479 except Exception:
2480 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002481 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002482 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002483 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002484 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002485
Jimmie Westera0444582012-10-24 13:44:42 +02002486 def _UpdateHooks(self):
2487 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002488 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002489
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002490 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002491 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002492 if not os.path.exists(hooks):
2493 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002494 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002495 name = os.path.basename(stock_hook)
2496
Victor Boivie65e0f352011-04-18 11:23:29 +02002497 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002498 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002499 # Don't install a Gerrit Code Review hook if this
2500 # project does not appear to use it for reviews.
2501 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002502 # Since the manifest project is one of those, but also
2503 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002504 continue
2505
2506 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002507 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002508 continue
2509 if os.path.exists(dst):
2510 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002511 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002512 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002513 _warn("%s: Not replacing locally modified %s hook",
2514 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002515 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002516 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002517 platform_utils.symlink(
2518 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002519 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002520 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002521 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002522 else:
2523 raise
2524
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002525 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002526 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002527 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002528 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002529 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002530 remote.review = self.remote.review
2531 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002532
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002533 if self.worktree:
2534 remote.ResetFetch(mirror=False)
2535 else:
2536 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002537 remote.Save()
2538
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539 def _InitMRef(self):
2540 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002541 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002542
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002543 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002544 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002545
2546 def _InitAnyMRef(self, ref):
2547 cur = self.bare_ref.symref(ref)
2548
2549 if self.revisionId:
2550 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2551 msg = 'manifest set to %s' % self.revisionId
2552 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002553 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002554 else:
2555 remote = self.GetRemote(self.remote.name)
2556 dst = remote.ToLocal(self.revisionExpr)
2557 if cur != dst:
2558 msg = 'manifest set to %s' % self.revisionExpr
2559 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002560
Kevin Degi384b3c52014-10-16 16:02:58 -06002561 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002562 symlink_files = self.shareable_files[:]
2563 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002564 if share_refs:
2565 symlink_files += self.working_tree_files
2566 symlink_dirs += self.working_tree_dirs
2567 to_symlink = symlink_files + symlink_dirs
2568 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002569 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002570 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002571 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002572 # Fail if the links are pointing to the wrong place
2573 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002574 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002575 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002576 'work tree. If you\'re comfortable with the '
2577 'possibility of losing the work tree\'s git metadata,'
2578 ' use `repo sync --force-sync {0}` to '
2579 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002580
David James8d201162013-10-11 17:03:19 -07002581 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2582 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2583
2584 Args:
2585 gitdir: The bare git repository. Must already be initialized.
2586 dotgit: The repository you would like to initialize.
2587 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2588 Only one work tree can store refs under a given |gitdir|.
2589 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2590 This saves you the effort of initializing |dotgit| yourself.
2591 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002592 symlink_files = self.shareable_files[:]
2593 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002594 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002595 symlink_files += self.working_tree_files
2596 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002597 to_symlink = symlink_files + symlink_dirs
2598
2599 to_copy = []
2600 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002601 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002602
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002603 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002604 for name in set(to_copy).union(to_symlink):
2605 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002606 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002607 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002608
Kevin Degi384b3c52014-10-16 16:02:58 -06002609 if os.path.lexists(dst):
2610 continue
David James8d201162013-10-11 17:03:19 -07002611
2612 # If the source dir doesn't exist, create an empty dir.
2613 if name in symlink_dirs and not os.path.lexists(src):
2614 os.makedirs(src)
2615
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002616 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002617 platform_utils.symlink(
2618 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002619 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002620 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002621 shutil.copytree(src, dst)
2622 elif os.path.isfile(src):
2623 shutil.copy(src, dst)
2624
Conley Owens80b87fe2014-05-09 17:13:44 -07002625 # If the source file doesn't exist, ensure the destination
2626 # file doesn't either.
2627 if name in symlink_files and not os.path.lexists(src):
2628 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002629 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002630 except OSError:
2631 pass
2632
David James8d201162013-10-11 17:03:19 -07002633 except OSError as e:
2634 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002635 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002636 else:
2637 raise
2638
Martin Kellye4e94d22017-03-21 16:05:12 -07002639 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002640 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002641 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002642 try:
2643 if init_dotgit:
2644 os.makedirs(dotgit)
2645 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2646 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002647
Kevin Degiabaa7f32014-11-12 11:27:45 -07002648 try:
2649 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2650 except GitError as e:
2651 if force_sync:
2652 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002653 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002654 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002655 except:
2656 raise e
2657 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002658
Kevin Degib1a07b82015-07-27 13:33:43 -06002659 if init_dotgit:
2660 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002661
Kevin Degib1a07b82015-07-27 13:33:43 -06002662 cmd = ['read-tree', '--reset', '-u']
2663 cmd.append('-v')
2664 cmd.append(HEAD)
2665 if GitCommand(self, cmd).Wait() != 0:
2666 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002667
Martin Kellye4e94d22017-03-21 16:05:12 -07002668 if submodules:
2669 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002670 self._CopyAndLinkFiles()
2671 except Exception:
2672 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002673 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002674 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002675
Renaud Paquay788e9622017-01-27 11:41:12 -08002676 def _get_symlink_error_message(self):
2677 if platform_utils.isWindows():
2678 return ('Unable to create symbolic link. Please re-run the command as '
2679 'Administrator, or see '
2680 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2681 'for other options.')
2682 return 'filesystem must support symlinks'
2683
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002684 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002685 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002686
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002687 def _revlist(self, *args, **kw):
2688 a = []
2689 a.extend(args)
2690 a.append('--')
2691 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002692
2693 @property
2694 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002695 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002696
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002697 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002698 """Get logs between two revisions of this project."""
2699 comp = '..'
2700 if rev1:
2701 revs = [rev1]
2702 if rev2:
2703 revs.extend([comp, rev2])
2704 cmd = ['log', ''.join(revs)]
2705 out = DiffColoring(self.config)
2706 if out.is_on and color:
2707 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002708 if pretty_format is not None:
2709 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002710 if oneline:
2711 cmd.append('--oneline')
2712
2713 try:
2714 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2715 if log.Wait() == 0:
2716 return log.stdout
2717 except GitError:
2718 # worktree may not exist if groups changed for example. In that case,
2719 # try in gitdir instead.
2720 if not os.path.exists(self.worktree):
2721 return self.bare_git.log(*cmd[1:])
2722 else:
2723 raise
2724 return None
2725
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002726 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2727 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002728 """Get the list of logs from this revision to given revisionId"""
2729 logs = {}
2730 selfId = self.GetRevisionId(self._allrefs)
2731 toId = toProject.GetRevisionId(toProject._allrefs)
2732
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002733 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2734 pretty_format=pretty_format)
2735 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2736 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002737 return logs
2738
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002739 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002740
David James8d201162013-10-11 17:03:19 -07002741 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002742 self._project = project
2743 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002744 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002745
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746 def LsOthers(self):
2747 p = GitCommand(self._project,
2748 ['ls-files',
2749 '-z',
2750 '--others',
2751 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002752 bare=False,
David James8d201162013-10-11 17:03:19 -07002753 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002754 capture_stdout=True,
2755 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002756 if p.Wait() == 0:
2757 out = p.stdout
2758 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002759 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002760 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002761 return []
2762
2763 def DiffZ(self, name, *args):
2764 cmd = [name]
2765 cmd.append('-z')
2766 cmd.extend(args)
2767 p = GitCommand(self._project,
2768 cmd,
David James8d201162013-10-11 17:03:19 -07002769 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002770 bare=False,
2771 capture_stdout=True,
2772 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002773 try:
2774 out = p.process.stdout.read()
2775 r = {}
2776 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002777 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002778 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002779 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002780 info = next(out)
2781 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002782 except StopIteration:
2783 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002784
2785 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002786
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002787 def __init__(self, path, omode, nmode, oid, nid, state):
2788 self.path = path
2789 self.src_path = None
2790 self.old_mode = omode
2791 self.new_mode = nmode
2792 self.old_id = oid
2793 self.new_id = nid
2794
2795 if len(state) == 1:
2796 self.status = state
2797 self.level = None
2798 else:
2799 self.status = state[:1]
2800 self.level = state[1:]
2801 while self.level.startswith('0'):
2802 self.level = self.level[1:]
2803
2804 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002805 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002806 if info.status in ('R', 'C'):
2807 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002808 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002809 r[info.path] = info
2810 return r
2811 finally:
2812 p.Wait()
2813
2814 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002815 if self._bare:
2816 path = os.path.join(self._project.gitdir, HEAD)
2817 else:
2818 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002819 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002820 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002821 except IOError as e:
2822 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002823 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002824 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002825 finally:
2826 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302827 try:
2828 line = line.decode()
2829 except AttributeError:
2830 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002831 if line.startswith('ref: '):
2832 return line[5:-1]
2833 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834
2835 def SetHead(self, ref, message=None):
2836 cmdv = []
2837 if message is not None:
2838 cmdv.extend(['-m', message])
2839 cmdv.append(HEAD)
2840 cmdv.append(ref)
2841 self.symbolic_ref(*cmdv)
2842
2843 def DetachHead(self, new, message=None):
2844 cmdv = ['--no-deref']
2845 if message is not None:
2846 cmdv.extend(['-m', message])
2847 cmdv.append(HEAD)
2848 cmdv.append(new)
2849 self.update_ref(*cmdv)
2850
2851 def UpdateRef(self, name, new, old=None,
2852 message=None,
2853 detach=False):
2854 cmdv = []
2855 if message is not None:
2856 cmdv.extend(['-m', message])
2857 if detach:
2858 cmdv.append('--no-deref')
2859 cmdv.append(name)
2860 cmdv.append(new)
2861 if old is not None:
2862 cmdv.append(old)
2863 self.update_ref(*cmdv)
2864
2865 def DeleteRef(self, name, old=None):
2866 if not old:
2867 old = self.rev_parse(name)
2868 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002869 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002870
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002871 def rev_list(self, *args, **kw):
2872 if 'format' in kw:
2873 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2874 else:
2875 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002876 cmdv.extend(args)
2877 p = GitCommand(self._project,
2878 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002879 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002880 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002881 capture_stdout=True,
2882 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002883 r = []
2884 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002885 if line[-1] == '\n':
2886 line = line[:-1]
2887 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002888 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002889 raise GitError('%s rev-list %s: %s' %
2890 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002891 return r
2892
2893 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002894 """Allow arbitrary git commands using pythonic syntax.
2895
2896 This allows you to do things like:
2897 git_obj.rev_parse('HEAD')
2898
2899 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2900 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002901 Any other positional arguments will be passed to the git command, and the
2902 following keyword arguments are supported:
2903 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002904
2905 Args:
2906 name: The name of the git command to call. Any '_' characters will
2907 be replaced with '-'.
2908
2909 Returns:
2910 A callable object that will try to call git with the named command.
2911 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002912 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002913
Dave Borowitz091f8932012-10-23 17:01:04 -07002914 def runner(*args, **kwargs):
2915 cmdv = []
2916 config = kwargs.pop('config', None)
2917 for k in kwargs:
2918 raise TypeError('%s() got an unexpected keyword argument %r'
2919 % (name, k))
2920 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002921 if not git_require((1, 7, 2)):
2922 raise ValueError('cannot set config on command line for %s()'
2923 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302924 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002925 cmdv.append('-c')
2926 cmdv.append('%s=%s' % (k, v))
2927 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002928 cmdv.extend(args)
2929 p = GitCommand(self._project,
2930 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002931 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002932 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002933 capture_stdout=True,
2934 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002935 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002936 raise GitError('%s %s: %s' %
2937 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002938 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302939 try:
Conley Owensedd01512013-09-26 12:59:58 -07002940 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302941 except AttributeError:
2942 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2944 return r[:-1]
2945 return r
2946 return runner
2947
2948
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002949class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002950
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002951 def __str__(self):
2952 return 'prior sync failed; rebase still in progress'
2953
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002954
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002955class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002956
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002957 def __str__(self):
2958 return 'contains uncommitted changes'
2959
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002960
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002961class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002962
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002963 def __init__(self, project, text):
2964 self.project = project
2965 self.text = text
2966
2967 def Print(self, syncbuf):
2968 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2969 syncbuf.out.nl()
2970
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002971
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002972class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002973
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002974 def __init__(self, project, why):
2975 self.project = project
2976 self.why = why
2977
2978 def Print(self, syncbuf):
2979 syncbuf.out.fail('error: %s/: %s',
2980 self.project.relpath,
2981 str(self.why))
2982 syncbuf.out.nl()
2983
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002984
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002985class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002986
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002987 def __init__(self, project, action):
2988 self.project = project
2989 self.action = action
2990
2991 def Run(self, syncbuf):
2992 out = syncbuf.out
2993 out.project('project %s/', self.project.relpath)
2994 out.nl()
2995 try:
2996 self.action()
2997 out.nl()
2998 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002999 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003000 out.nl()
3001 return False
3002
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003003
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003005
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003006 def __init__(self, config):
3007 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003008 self.project = self.printer('header', attr='bold')
3009 self.info = self.printer('info')
3010 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003011
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003012
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003013class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003014
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003015 def __init__(self, config, detach_head=False):
3016 self._messages = []
3017 self._failures = []
3018 self._later_queue1 = []
3019 self._later_queue2 = []
3020
3021 self.out = _SyncColoring(config)
3022 self.out.redirect(sys.stderr)
3023
3024 self.detach_head = detach_head
3025 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003026 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003027
3028 def info(self, project, fmt, *args):
3029 self._messages.append(_InfoMessage(project, fmt % args))
3030
3031 def fail(self, project, err=None):
3032 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003033 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003034
3035 def later1(self, project, what):
3036 self._later_queue1.append(_Later(project, what))
3037
3038 def later2(self, project, what):
3039 self._later_queue2.append(_Later(project, what))
3040
3041 def Finish(self):
3042 self._PrintMessages()
3043 self._RunLater()
3044 self._PrintMessages()
3045 return self.clean
3046
David Rileye0684ad2017-04-05 00:02:59 -07003047 def Recently(self):
3048 recent_clean = self.recent_clean
3049 self.recent_clean = True
3050 return recent_clean
3051
3052 def _MarkUnclean(self):
3053 self.clean = False
3054 self.recent_clean = False
3055
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003056 def _RunLater(self):
3057 for q in ['_later_queue1', '_later_queue2']:
3058 if not self._RunQueue(q):
3059 return
3060
3061 def _RunQueue(self, queue):
3062 for m in getattr(self, queue):
3063 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003064 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003065 return False
3066 setattr(self, queue, [])
3067 return True
3068
3069 def _PrintMessages(self):
3070 for m in self._messages:
3071 m.Print(self)
3072 for m in self._failures:
3073 m.Print(self)
3074
3075 self._messages = []
3076 self._failures = []
3077
3078
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003079class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003081 """A special project housed under .repo.
3082 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003083
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003084 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003085 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003086 manifest=manifest,
3087 name=name,
3088 gitdir=gitdir,
3089 objdir=gitdir,
3090 worktree=worktree,
3091 remote=RemoteSpec('origin'),
3092 relpath='.repo/%s' % name,
3093 revisionExpr='refs/heads/master',
3094 revisionId=None,
3095 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003096
3097 def PreSync(self):
3098 if self.Exists:
3099 cb = self.CurrentBranch
3100 if cb:
3101 base = self.GetBranch(cb).merge
3102 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003103 self.revisionExpr = base
3104 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003105
Martin Kelly224a31a2017-07-10 14:46:25 -07003106 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003107 """ Prepare MetaProject for manifest branch switch
3108 """
3109
3110 # detach and delete manifest branch, allowing a new
3111 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003112 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003113 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003114 syncbuf.Finish()
3115
3116 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003117 ['update-ref', '-d', 'refs/heads/default'],
3118 capture_stdout=True,
3119 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003120
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003121 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003122 def LastFetch(self):
3123 try:
3124 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3125 return os.path.getmtime(fh)
3126 except OSError:
3127 return 0
3128
3129 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003130 def HasChanges(self):
3131 """Has the remote received new commits not yet checked out?
3132 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003133 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003134 return False
3135
David Pursehouse8a68ff92012-09-24 12:15:13 +09003136 all_refs = self.bare_ref.all
3137 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003138 head = self.work_git.GetHead()
3139 if head.startswith(R_HEADS):
3140 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003141 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003142 except KeyError:
3143 head = None
3144
3145 if revid == head:
3146 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003147 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003148 return True
3149 return False