blob: 5c3c2ece2158ca704b056611cc24afa6741b50a7 [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
David Pursehouse59bbb582013-05-17 10:49:33 +090052 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090054 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053055
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070056
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070057def _lwrite(path, content):
58 lock = '%s.lock' % path
59
Chirayu Desai303a82f2014-08-19 22:57:17 +053060 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 try:
62 fd.write(content)
63 finally:
64 fd.close()
65
66 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070067 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080069 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070070 raise
71
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070072
Shawn O. Pearce48244782009-04-16 08:25:57 -070073def _error(fmt, *args):
74 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070075 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070076
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
David Pursehousef33929d2015-08-24 14:39:14 +090078def _warn(fmt, *args):
79 msg = fmt % args
80 print('warn: %s' % msg, file=sys.stderr)
81
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070082
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083def not_rev(r):
84 return '^' + r
85
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070086
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080087def sq(r):
88 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080089
Jonathan Nieder93719792015-03-17 11:29:58 -070090_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070091
92
Jonathan Nieder93719792015-03-17 11:29:58 -070093def _ProjectHooks():
94 """List the hooks present in the 'hooks' directory.
95
96 These hooks are project hooks and are copied to the '.git/hooks' directory
97 of all subprojects.
98
99 This function caches the list of hooks (based on the contents of the
100 'repo/hooks' directory) on the first call.
101
102 Returns:
103 A list of absolute paths to all of the files in the hooks directory.
104 """
105 global _project_hook_list
106 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700107 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 d = os.path.join(d, 'hooks')
109 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
110 return _project_hook_list
111
112
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700113class DownloadedChange(object):
114 _commit_cache = None
115
116 def __init__(self, project, base, change_id, ps_id, commit):
117 self.project = project
118 self.base = base
119 self.change_id = change_id
120 self.ps_id = ps_id
121 self.commit = commit
122
123 @property
124 def commits(self):
125 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700126 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
127 '--abbrev-commit',
128 '--pretty=oneline',
129 '--reverse',
130 '--date-order',
131 not_rev(self.base),
132 self.commit,
133 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700134 return self._commit_cache
135
136
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137class ReviewableBranch(object):
138 _commit_cache = None
139
140 def __init__(self, project, branch, base):
141 self.project = project
142 self.branch = branch
143 self.base = base
144
145 @property
146 def name(self):
147 return self.branch.name
148
149 @property
150 def commits(self):
151 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700152 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
153 '--abbrev-commit',
154 '--pretty=oneline',
155 '--reverse',
156 '--date-order',
157 not_rev(self.base),
158 R_HEADS + self.name,
159 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700160 return self._commit_cache
161
162 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800163 def unabbrev_commits(self):
164 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700165 for commit in self.project.bare_git.rev_list(not_rev(self.base),
166 R_HEADS + self.name,
167 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800168 r[commit[0:8]] = commit
169 return r
170
171 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700173 return self.project.bare_git.log('--pretty=format:%cd',
174 '-n', '1',
175 R_HEADS + self.name,
176 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700178 def UploadForReview(self, people,
179 auto_topic=False,
180 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 private=False,
182 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200183 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800184 validate_certs=True,
185 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800186 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700187 people,
Brian Harring435370c2012-07-28 15:37:04 -0700188 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400189 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200190 private=private,
191 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)
259 if not os.path.isdir(dest_dir):
260 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)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 if not os.path.isdir(dest_dir):
289 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
308 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
309 _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,
664 clone_depth=None,
665 upstream=None,
666 parent=None,
667 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900668 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700669 optimized_fetch=False,
670 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800671 """Init a Project object.
672
673 Args:
674 manifest: The XmlManifest object.
675 name: The `name` attribute of manifest.xml's project element.
676 remote: RemoteSpec object specifying its remote's properties.
677 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700678 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800679 worktree: Absolute path of git working tree.
680 relpath: Relative path of git working tree to repo's top directory.
681 revisionExpr: The `revision` attribute of manifest.xml's project element.
682 revisionId: git commit id for checking out.
683 rebase: The `rebase` attribute of manifest.xml's project element.
684 groups: The `groups` attribute of manifest.xml's project element.
685 sync_c: The `sync-c` attribute of manifest.xml's project element.
686 sync_s: The `sync-s` attribute of manifest.xml's project element.
687 upstream: The `upstream` attribute of manifest.xml's project element.
688 parent: The parent Project object.
689 is_derived: False if the project was explicitly defined in the manifest;
690 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400691 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900692 optimized_fetch: If True, when a project is set to a sha1 revision, only
693 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700694 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800695 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700696 self.manifest = manifest
697 self.name = name
698 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800699 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700700 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800701 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700702 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800703 else:
704 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700705 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700706 self.revisionExpr = revisionExpr
707
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700708 if revisionId is None \
709 and revisionExpr \
710 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700711 self.revisionId = revisionExpr
712 else:
713 self.revisionId = revisionId
714
Mike Pontillod3153822012-02-28 11:53:24 -0800715 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700716 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700717 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800718 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900719 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700720 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800721 self.parent = parent
722 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900723 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800725
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500728 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500729 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700730 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
731 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800733 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700734 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800735 else:
736 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700737 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700738 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700739 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400740 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700741 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742
Doug Anderson37282b42011-03-04 11:54:18 -0800743 # This will be filled in if a project is later identified to be the
744 # project containing repo hooks.
745 self.enabled_repo_hooks = []
746
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800748 def Derived(self):
749 return self.is_derived
750
751 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700752 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600753 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754
755 @property
756 def CurrentBranch(self):
757 """Obtain the name of the currently checked out branch.
758 The branch name omits the 'refs/heads/' prefix.
759 None is returned if the project is on a detached HEAD.
760 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700761 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762 if b.startswith(R_HEADS):
763 return b[len(R_HEADS):]
764 return None
765
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700766 def IsRebaseInProgress(self):
767 w = self.worktree
768 g = os.path.join(w, '.git')
769 return os.path.exists(os.path.join(g, 'rebase-apply')) \
770 or os.path.exists(os.path.join(g, 'rebase-merge')) \
771 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200772
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773 def IsDirty(self, consider_untracked=True):
774 """Is the working directory modified in some way?
775 """
776 self.work_git.update_index('-q',
777 '--unmerged',
778 '--ignore-missing',
779 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900780 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700781 return True
782 if self.work_git.DiffZ('diff-files'):
783 return True
784 if consider_untracked and self.work_git.LsOthers():
785 return True
786 return False
787
788 _userident_name = None
789 _userident_email = None
790
791 @property
792 def UserName(self):
793 """Obtain the user's personal name.
794 """
795 if self._userident_name is None:
796 self._LoadUserIdentity()
797 return self._userident_name
798
799 @property
800 def UserEmail(self):
801 """Obtain the user's email address. This is very likely
802 to be their Gerrit login.
803 """
804 if self._userident_email is None:
805 self._LoadUserIdentity()
806 return self._userident_email
807
808 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900809 u = self.bare_git.var('GIT_COMMITTER_IDENT')
810 m = re.compile("^(.*) <([^>]*)> ").match(u)
811 if m:
812 self._userident_name = m.group(1)
813 self._userident_email = m.group(2)
814 else:
815 self._userident_name = ''
816 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817
818 def GetRemote(self, name):
819 """Get the configuration for a single remote.
820 """
821 return self.config.GetRemote(name)
822
823 def GetBranch(self, name):
824 """Get the configuration for a single branch.
825 """
826 return self.config.GetBranch(name)
827
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 def GetBranches(self):
829 """Get all existing local branches.
830 """
831 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900832 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700833 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700834
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530835 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836 if name.startswith(R_HEADS):
837 name = name[len(R_HEADS):]
838 b = self.GetBranch(name)
839 b.current = name == current
840 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900841 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700842 heads[name] = b
843
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530844 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845 if name.startswith(R_PUB):
846 name = name[len(R_PUB):]
847 b = heads.get(name)
848 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900849 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700850
851 return heads
852
Colin Cross5acde752012-03-28 20:15:45 -0700853 def MatchesGroups(self, manifest_groups):
854 """Returns true if the manifest groups specified at init should cause
855 this project to be synced.
856 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700857 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700858
859 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700861 manifest_groups: "-group1,group2"
862 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500863
864 The special manifest group "default" will match any project that
865 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700866 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500867 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700869 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500870 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871
Conley Owens971de8e2012-04-16 10:36:08 -0700872 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700873 for group in expanded_manifest_groups:
874 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700875 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700877 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700878
Conley Owens971de8e2012-04-16 10:36:08 -0700879 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700881# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700882 def UncommitedFiles(self, get_all=True):
883 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 Args:
886 get_all: a boolean, if True - get information about all different
887 uncommitted files. If False - return as soon as any kind of
888 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500889 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700890 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500891 self.work_git.update_index('-q',
892 '--unmerged',
893 '--ignore-missing',
894 '--refresh')
895 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700896 details.append("rebase in progress")
897 if not get_all:
898 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500899
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700900 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
901 if changes:
902 details.extend(changes)
903 if not get_all:
904 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500905
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700906 changes = self.work_git.DiffZ('diff-files').keys()
907 if changes:
908 details.extend(changes)
909 if not get_all:
910 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500911
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700912 changes = self.work_git.LsOthers()
913 if changes:
914 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500915
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700916 return details
917
918 def HasChanges(self):
919 """Returns true if there are uncommitted changes.
920 """
921 if self.UncommitedFiles(get_all=False):
922 return True
923 else:
924 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500925
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600926 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200928
929 Args:
930 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600931 quiet: If True then only print the project name. Do not print
932 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933 """
934 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700935 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200936 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700937 print(file=output_redir)
938 print('project %s/' % self.relpath, file=output_redir)
939 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700940 return
941
942 self.work_git.update_index('-q',
943 '--unmerged',
944 '--ignore-missing',
945 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700946 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
948 df = self.work_git.DiffZ('diff-files')
949 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100950 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700951 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
953 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700954 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200955 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700956 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600958 if quiet:
959 out.nl()
960 return 'DIRTY'
961
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700962 branch = self.CurrentBranch
963 if branch is None:
964 out.nobranch('(*** NO BRANCH ***)')
965 else:
966 out.branch('branch %s', branch)
967 out.nl()
968
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700969 if rb:
970 out.important('prior sync failed; rebase still in progress')
971 out.nl()
972
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973 paths = list()
974 paths.extend(di.keys())
975 paths.extend(df.keys())
976 paths.extend(do)
977
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530978 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900979 try:
980 i = di[p]
981 except KeyError:
982 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700983
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 try:
985 f = df[p]
986 except KeyError:
987 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 if i:
990 i_status = i.status.upper()
991 else:
992 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900994 if f:
995 f_status = f.status.lower()
996 else:
997 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700998
999 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001000 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001001 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002 else:
1003 line = ' %s%s\t%s' % (i_status, f_status, p)
1004
1005 if i and not f:
1006 out.added('%s', line)
1007 elif (i and f) or (not i and f):
1008 out.changed('%s', line)
1009 elif not i and not f:
1010 out.untracked('%s', line)
1011 else:
1012 out.write('%s', line)
1013 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001014
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001015 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016
pelyad67872d2012-03-28 14:49:58 +03001017 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018 """Prints the status of the repository to stdout.
1019 """
1020 out = DiffColoring(self.config)
1021 cmd = ['diff']
1022 if out.is_on:
1023 cmd.append('--color')
1024 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001025 if absolute_paths:
1026 cmd.append('--src-prefix=a/%s/' % self.relpath)
1027 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028 cmd.append('--')
1029 p = GitCommand(self,
1030 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001031 capture_stdout=True,
1032 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 has_diff = False
1034 for line in p.process.stdout:
1035 if not has_diff:
1036 out.nl()
1037 out.project('project %s/' % self.relpath)
1038 out.nl()
1039 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001040 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041 p.Wait()
1042
1043
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001044# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045
David Pursehouse8a68ff92012-09-24 12:15:13 +09001046 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 """Was the branch published (uploaded) for code review?
1048 If so, returns the SHA-1 hash of the last published
1049 state for the branch.
1050 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001051 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001052 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001053 try:
1054 return self.bare_git.rev_parse(key)
1055 except GitError:
1056 return None
1057 else:
1058 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001059 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001060 except KeyError:
1061 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001062
David Pursehouse8a68ff92012-09-24 12:15:13 +09001063 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064 """Prunes any stale published refs.
1065 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 if all_refs is None:
1067 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 heads = set()
1069 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301070 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 if name.startswith(R_HEADS):
1072 heads.add(name)
1073 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001074 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301076 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 n = name[len(R_PUB):]
1078 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001079 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001081 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 """List any branches which can be uploaded for review.
1083 """
1084 heads = {}
1085 pubed = {}
1086
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301087 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001089 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001091 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092
1093 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301094 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001095 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001096 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001097 if selected_branch and branch != selected_branch:
1098 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001099
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001100 rb = self.GetUploadableBranch(branch)
1101 if rb:
1102 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103 return ready
1104
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001105 def GetUploadableBranch(self, branch_name):
1106 """Get a single uploadable branch, or None.
1107 """
1108 branch = self.GetBranch(branch_name)
1109 base = branch.LocalMerge
1110 if branch.LocalMerge:
1111 rb = ReviewableBranch(self, branch, base)
1112 if rb.commits:
1113 return rb
1114 return None
1115
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001116 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001117 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001118 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001119 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001120 private=False,
1121 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001122 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001123 validate_certs=True,
1124 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001125 """Uploads the named branch for code review.
1126 """
1127 if branch is None:
1128 branch = self.CurrentBranch
1129 if branch is None:
1130 raise GitError('not currently on a branch')
1131
1132 branch = self.GetBranch(branch)
1133 if not branch.LocalMerge:
1134 raise GitError('branch %s does not track a remote' % branch.name)
1135 if not branch.remote.review:
1136 raise GitError('remote %s has no review url' % branch.remote.name)
1137
Bryan Jacobsf609f912013-05-06 13:36:24 -04001138 if dest_branch is None:
1139 dest_branch = self.dest_branch
1140 if dest_branch is None:
1141 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001142 if not dest_branch.startswith(R_HEADS):
1143 dest_branch = R_HEADS + dest_branch
1144
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001145 if not branch.remote.projectname:
1146 branch.remote.projectname = self.name
1147 branch.remote.Save()
1148
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001149 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001150 if url is None:
1151 raise UploadError('review not configured')
1152 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001153
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001154 if url.startswith('ssh://'):
Mike Frysingercb099b32018-12-13 13:08:57 -05001155 rp = ['gerrit receive-pack']
1156 for e in people[0]:
1157 rp.append('--reviewer=%s' % sq(e))
1158 for e in people[1]:
1159 rp.append('--cc=%s' % sq(e))
1160 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001161
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001162 for push_option in (push_options or []):
1163 cmd.append('-o')
1164 cmd.append(push_option)
1165
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001167
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001168 if dest_branch.startswith(R_HEADS):
1169 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001170
1171 upload_type = 'for'
1172 if draft:
1173 upload_type = 'drafts'
1174
1175 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1176 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001177 if auto_topic:
1178 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001179
Mike Frysingercb099b32018-12-13 13:08:57 -05001180 if not url.startswith('ssh://'):
1181 rp = ['r=%s' % p for p in people[0]] + \
1182 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001183 if private:
1184 rp = rp + ['private']
1185 if wip:
1186 rp = rp + ['wip']
Mike Frysingercb099b32018-12-13 13:08:57 -05001187 if rp:
1188 ref_spec = ref_spec + '%' + ','.join(rp)
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:
1334 alt_dir = fd.readline().rstrip()
1335 finally:
1336 fd.close()
1337 except IOError:
1338 alt_dir = None
1339 else:
1340 alt_dir = None
1341
Ningning Xiac2fbc782016-08-22 14:24:39 -07001342 applied_cache = False
1343 # If cache_dir is provided, and it's a new repository without
1344 # alternative_dir, bootstrap this project repo with the git
1345 # cache files.
1346 if cache_dir is not None and is_new and alt_dir is None:
1347 try:
1348 self.CacheApply(cache_dir)
1349 applied_cache = True
1350 is_new = False
1351 except CacheApplyError as e:
1352 _error('Could not apply git cache: %s', e)
1353 _error('Please check if you have the right GS credentials.')
1354 _error('Please check if the cache files exist in GS.')
1355
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001356 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001357 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001358 and alt_dir is None \
1359 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001360 is_new = False
1361
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001362 if not current_branch_only:
1363 if self.sync_c:
1364 current_branch_only = True
1365 elif not self.manifest._loaded:
1366 # Manifest cannot check defaults until it syncs.
1367 current_branch_only = False
1368 elif self.manifest.default.sync_c:
1369 current_branch_only = True
1370
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001371 if self.clone_depth:
1372 depth = self.clone_depth
1373 else:
1374 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1375
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001376 need_to_fetch = not (optimized_fetch and
1377 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001378 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001379 if (need_to_fetch and
1380 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1381 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001382 no_tags=no_tags, prune=prune, depth=depth,
1383 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001384 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001385
1386 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001387 self._InitMRef()
1388 else:
1389 self._InitMirrorHead()
1390 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001391 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001392 except OSError:
1393 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001394 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001395
1396 def PostRepoUpgrade(self):
1397 self._InitHooks()
1398
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001399 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001400 if self.manifest.isGitcClient:
1401 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001402 for copyfile in self.copyfiles:
1403 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001404 for linkfile in self.linkfiles:
1405 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001406
Julien Camperguedd654222014-01-09 16:21:37 +01001407 def GetCommitRevisionId(self):
1408 """Get revisionId of a commit.
1409
1410 Use this method instead of GetRevisionId to get the id of the commit rather
1411 than the id of the current git object (for example, a tag)
1412
1413 """
1414 if not self.revisionExpr.startswith(R_TAGS):
1415 return self.GetRevisionId(self._allrefs)
1416
1417 try:
1418 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1419 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001420 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1421 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001422
David Pursehouse8a68ff92012-09-24 12:15:13 +09001423 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001424 if self.revisionId:
1425 return self.revisionId
1426
1427 rem = self.GetRemote(self.remote.name)
1428 rev = rem.ToLocal(self.revisionExpr)
1429
David Pursehouse8a68ff92012-09-24 12:15:13 +09001430 if all_refs is not None and rev in all_refs:
1431 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001432
1433 try:
1434 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1435 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001436 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1437 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001438
Martin Kellye4e94d22017-03-21 16:05:12 -07001439 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001440 """Perform only the local IO portion of the sync process.
1441 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001442 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001443 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001444 all_refs = self.bare_ref.all
1445 self.CleanPublishedCache(all_refs)
1446 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001447
David Pursehouse1d947b32012-10-25 12:23:11 +09001448 def _doff():
1449 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001450 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001451
Martin Kellye4e94d22017-03-21 16:05:12 -07001452 def _dosubmodules():
1453 self._SyncSubmodules(quiet=True)
1454
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001455 head = self.work_git.GetHead()
1456 if head.startswith(R_HEADS):
1457 branch = head[len(R_HEADS):]
1458 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001459 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001460 except KeyError:
1461 head = None
1462 else:
1463 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001464
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001465 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001466 # Currently on a detached HEAD. The user is assumed to
1467 # not have any local modifications worth worrying about.
1468 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001469 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001470 syncbuf.fail(self, _PriorSyncFailedError())
1471 return
1472
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001473 if head == revid:
1474 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001475 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001476 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001477 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001478 # The copy/linkfile config may have changed.
1479 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001480 return
1481 else:
1482 lost = self._revlist(not_rev(revid), HEAD)
1483 if lost:
1484 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001485
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001486 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001487 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001488 if submodules:
1489 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001490 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001491 syncbuf.fail(self, e)
1492 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001493 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001494 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001495
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001496 if head == revid:
1497 # No changes; don't do anything further.
1498 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001499 # The copy/linkfile config may have changed.
1500 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001501 return
1502
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001503 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001504
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001505 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001507 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001508 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001509 syncbuf.info(self,
1510 "leaving %s; does not track upstream",
1511 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001512 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001513 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001514 if submodules:
1515 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001516 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001517 syncbuf.fail(self, e)
1518 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001519 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001520 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001521
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001522 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001523 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001525 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001526 if not_merged:
1527 if upstream_gain:
1528 # The user has published this branch and some of those
1529 # commits are not yet merged upstream. We do not want
1530 # to rewrite the published commits so we punt.
1531 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001532 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001533 "branch %s is published (but not merged) and is now "
1534 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001535 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001536 elif pub == head:
1537 # All published commits are merged, and thus we are a
1538 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001539 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001540 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001541 if submodules:
1542 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001543 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001545 # Examine the local commits not in the remote. Find the
1546 # last one attributed to this user, if any.
1547 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001548 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001549 last_mine = None
1550 cnt_mine = 0
1551 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301552 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001553 if committer_email == self.UserEmail:
1554 last_mine = commit_id
1555 cnt_mine += 1
1556
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001557 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001558 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559
1560 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001561 syncbuf.fail(self, _DirtyError())
1562 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001563
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001564 # If the upstream switched on us, warn the user.
1565 #
1566 if branch.merge != self.revisionExpr:
1567 if branch.merge and self.revisionExpr:
1568 syncbuf.info(self,
1569 'manifest switched %s...%s',
1570 branch.merge,
1571 self.revisionExpr)
1572 elif branch.merge:
1573 syncbuf.info(self,
1574 'manifest no longer tracks %s',
1575 branch.merge)
1576
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001577 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001578 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001579 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001580 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001581 syncbuf.info(self,
1582 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001583 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001584
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001585 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001586 if not ID_RE.match(self.revisionExpr):
1587 # in case of manifest sync the revisionExpr might be a SHA1
1588 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001589 if not branch.merge.startswith('refs/'):
1590 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001591 branch.Save()
1592
Mike Pontillod3153822012-02-28 11:53:24 -08001593 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001594 def _docopyandlink():
1595 self._CopyAndLinkFiles()
1596
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001597 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001598 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001599 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001600 if submodules:
1601 syncbuf.later2(self, _dosubmodules)
1602 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001603 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001604 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001605 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001606 if submodules:
1607 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001608 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001609 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001610 syncbuf.fail(self, e)
1611 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001612 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001613 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001614 if submodules:
1615 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001616
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001617 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001618 # dest should already be an absolute path, but src is project relative
1619 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001620 abssrc = os.path.join(self.worktree, src)
1621 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001622
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001623 def AddLinkFile(self, src, dest, absdest):
1624 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001625 # make src relative path to dest
1626 absdestdir = os.path.dirname(absdest)
1627 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001628 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001629
James W. Mills24c13082012-04-12 15:04:13 -05001630 def AddAnnotation(self, name, value, keep):
1631 self.annotations.append(_Annotation(name, value, keep))
1632
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001633 def DownloadPatchSet(self, change_id, patch_id):
1634 """Download a single patch set of a single change to FETCH_HEAD.
1635 """
1636 remote = self.GetRemote(self.remote.name)
1637
1638 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001639 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001640 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001641 if GitCommand(self, cmd, bare=True).Wait() != 0:
1642 return None
1643 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001644 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001645 change_id,
1646 patch_id,
1647 self.bare_git.rev_parse('FETCH_HEAD'))
1648
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001649
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001650# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001651
Simran Basib9a1b732015-08-20 12:19:28 -07001652 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001653 """Create a new branch off the manifest's revision.
1654 """
Simran Basib9a1b732015-08-20 12:19:28 -07001655 if not branch_merge:
1656 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001657 head = self.work_git.GetHead()
1658 if head == (R_HEADS + name):
1659 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001660
David Pursehouse8a68ff92012-09-24 12:15:13 +09001661 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001662 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001663 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001664 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001665 capture_stdout=True,
1666 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001667
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001668 branch = self.GetBranch(name)
1669 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001670 branch.merge = branch_merge
1671 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1672 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001673 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001674
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001675 if head.startswith(R_HEADS):
1676 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001677 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001678 except KeyError:
1679 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001680 if revid and head and revid == head:
1681 ref = os.path.join(self.gitdir, R_HEADS + name)
1682 try:
1683 os.makedirs(os.path.dirname(ref))
1684 except OSError:
1685 pass
1686 _lwrite(ref, '%s\n' % revid)
1687 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1688 'ref: %s%s\n' % (R_HEADS, name))
1689 branch.Save()
1690 return True
1691
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001692 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001693 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001694 capture_stdout=True,
1695 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001696 branch.Save()
1697 return True
1698 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001699
Wink Saville02d79452009-04-10 13:01:24 -07001700 def CheckoutBranch(self, name):
1701 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001702
1703 Args:
1704 name: The name of the branch to checkout.
1705
1706 Returns:
1707 True if the checkout succeeded; False if it didn't; None if the branch
1708 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001709 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001710 rev = R_HEADS + name
1711 head = self.work_git.GetHead()
1712 if head == rev:
1713 # Already on the branch
1714 #
1715 return True
Wink Saville02d79452009-04-10 13:01:24 -07001716
David Pursehouse8a68ff92012-09-24 12:15:13 +09001717 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001718 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001719 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001720 except KeyError:
1721 # Branch does not exist in this project
1722 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001723 return None
Wink Saville02d79452009-04-10 13:01:24 -07001724
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001725 if head.startswith(R_HEADS):
1726 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001727 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001728 except KeyError:
1729 head = None
1730
1731 if head == revid:
1732 # Same revision; just update HEAD to point to the new
1733 # target branch, but otherwise take no other action.
1734 #
1735 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1736 'ref: %s%s\n' % (R_HEADS, name))
1737 return True
1738
1739 return GitCommand(self,
1740 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001741 capture_stdout=True,
1742 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001743
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001744 def AbandonBranch(self, name):
1745 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001746
1747 Args:
1748 name: The name of the branch to abandon.
1749
1750 Returns:
1751 True if the abandon succeeded; False if it didn't; None if the branch
1752 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001753 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001754 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001755 all_refs = self.bare_ref.all
1756 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001757 # Doesn't exist
1758 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001759
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001760 head = self.work_git.GetHead()
1761 if head == rev:
1762 # We can't destroy the branch while we are sitting
1763 # on it. Switch to a detached HEAD.
1764 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001765 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001766
David Pursehouse8a68ff92012-09-24 12:15:13 +09001767 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001768 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001769 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1770 '%s\n' % revid)
1771 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001772 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001773
1774 return GitCommand(self,
1775 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001776 capture_stdout=True,
1777 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001778
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001779 def PruneHeads(self):
1780 """Prune any topic branches already merged into upstream.
1781 """
1782 cb = self.CurrentBranch
1783 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001784 left = self._allrefs
1785 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001786 if name.startswith(R_HEADS):
1787 name = name[len(R_HEADS):]
1788 if cb is None or name != cb:
1789 kill.append(name)
1790
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001791 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001792 if cb is not None \
1793 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001794 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001795 self.work_git.DetachHead(HEAD)
1796 kill.append(cb)
1797
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001798 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001799 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001800
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001801 try:
1802 self.bare_git.DetachHead(rev)
1803
1804 b = ['branch', '-d']
1805 b.extend(kill)
1806 b = GitCommand(self, b, bare=True,
1807 capture_stdout=True,
1808 capture_stderr=True)
1809 b.Wait()
1810 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001811 if ID_RE.match(old):
1812 self.bare_git.DetachHead(old)
1813 else:
1814 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001815 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001816
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001817 for branch in kill:
1818 if (R_HEADS + branch) not in left:
1819 self.CleanPublishedCache()
1820 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001821
1822 if cb and cb not in kill:
1823 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001824 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001825
1826 kept = []
1827 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001828 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001829 branch = self.GetBranch(branch)
1830 base = branch.LocalMerge
1831 if not base:
1832 base = rev
1833 kept.append(ReviewableBranch(self, branch, base))
1834 return kept
1835
1836
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001837# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001838
1839 def GetRegisteredSubprojects(self):
1840 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001841
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001842 def rec(subprojects):
1843 if not subprojects:
1844 return
1845 result.extend(subprojects)
1846 for p in subprojects:
1847 rec(p.subprojects)
1848 rec(self.subprojects)
1849 return result
1850
1851 def _GetSubmodules(self):
1852 # Unfortunately we cannot call `git submodule status --recursive` here
1853 # because the working tree might not exist yet, and it cannot be used
1854 # without a working tree in its current implementation.
1855
1856 def get_submodules(gitdir, rev):
1857 # Parse .gitmodules for submodule sub_paths and sub_urls
1858 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1859 if not sub_paths:
1860 return []
1861 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1862 # revision of submodule repository
1863 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1864 submodules = []
1865 for sub_path, sub_url in zip(sub_paths, sub_urls):
1866 try:
1867 sub_rev = sub_revs[sub_path]
1868 except KeyError:
1869 # Ignore non-exist submodules
1870 continue
1871 submodules.append((sub_rev, sub_path, sub_url))
1872 return submodules
1873
1874 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1875 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001876
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001877 def parse_gitmodules(gitdir, rev):
1878 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1879 try:
Anthony King7bdac712014-07-16 12:56:40 +01001880 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1881 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001882 except GitError:
1883 return [], []
1884 if p.Wait() != 0:
1885 return [], []
1886
1887 gitmodules_lines = []
1888 fd, temp_gitmodules_path = tempfile.mkstemp()
1889 try:
1890 os.write(fd, p.stdout)
1891 os.close(fd)
1892 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001893 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1894 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001895 if p.Wait() != 0:
1896 return [], []
1897 gitmodules_lines = p.stdout.split('\n')
1898 except GitError:
1899 return [], []
1900 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001901 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001902
1903 names = set()
1904 paths = {}
1905 urls = {}
1906 for line in gitmodules_lines:
1907 if not line:
1908 continue
1909 m = re_path.match(line)
1910 if m:
1911 names.add(m.group(1))
1912 paths[m.group(1)] = m.group(2)
1913 continue
1914 m = re_url.match(line)
1915 if m:
1916 names.add(m.group(1))
1917 urls[m.group(1)] = m.group(2)
1918 continue
1919 names = sorted(names)
1920 return ([paths.get(name, '') for name in names],
1921 [urls.get(name, '') for name in names])
1922
1923 def git_ls_tree(gitdir, rev, paths):
1924 cmd = ['ls-tree', rev, '--']
1925 cmd.extend(paths)
1926 try:
Anthony King7bdac712014-07-16 12:56:40 +01001927 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1928 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001929 except GitError:
1930 return []
1931 if p.Wait() != 0:
1932 return []
1933 objects = {}
1934 for line in p.stdout.split('\n'):
1935 if not line.strip():
1936 continue
1937 object_rev, object_path = line.split()[2:4]
1938 objects[object_path] = object_rev
1939 return objects
1940
1941 try:
1942 rev = self.GetRevisionId()
1943 except GitError:
1944 return []
1945 return get_submodules(self.gitdir, rev)
1946
1947 def GetDerivedSubprojects(self):
1948 result = []
1949 if not self.Exists:
1950 # If git repo does not exist yet, querying its submodules will
1951 # mess up its states; so return here.
1952 return result
1953 for rev, path, url in self._GetSubmodules():
1954 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001955 relpath, worktree, gitdir, objdir = \
1956 self.manifest.GetSubprojectPaths(self, name, path)
1957 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001958 if project:
1959 result.extend(project.GetDerivedSubprojects())
1960 continue
David James8d201162013-10-11 17:03:19 -07001961
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001962 if url.startswith('..'):
1963 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001964 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001965 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001966 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001967 review=self.remote.review,
1968 revision=self.remote.revision)
1969 subproject = Project(manifest=self.manifest,
1970 name=name,
1971 remote=remote,
1972 gitdir=gitdir,
1973 objdir=objdir,
1974 worktree=worktree,
1975 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001976 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001977 revisionId=rev,
1978 rebase=self.rebase,
1979 groups=self.groups,
1980 sync_c=self.sync_c,
1981 sync_s=self.sync_s,
1982 parent=self,
1983 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001984 result.append(subproject)
1985 result.extend(subproject.GetDerivedSubprojects())
1986 return result
1987
1988
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001989# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001990 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001991 try:
1992 # if revision (sha or tag) is not present then following function
1993 # throws an error.
1994 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1995 return True
1996 except GitError:
1997 # There is no such persistent revision. We have to fetch it.
1998 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001999
Julien Campergue335f5ef2013-10-16 11:02:35 +02002000 def _FetchArchive(self, tarpath, cwd=None):
2001 cmd = ['archive', '-v', '-o', tarpath]
2002 cmd.append('--remote=%s' % self.remote.url)
2003 cmd.append('--prefix=%s/' % self.relpath)
2004 cmd.append(self.revisionExpr)
2005
2006 command = GitCommand(self, cmd, cwd=cwd,
2007 capture_stdout=True,
2008 capture_stderr=True)
2009
2010 if command.Wait() != 0:
2011 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2012
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002013 def _RemoteFetch(self, name=None,
2014 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002015 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002016 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002017 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002018 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002019 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002020 depth=None,
2021 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002022
2023 is_sha1 = False
2024 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002025 # The depth should not be used when fetching to a mirror because
2026 # it will result in a shallow repository that cannot be cloned or
2027 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002028 # The repo project should also never be synced with partial depth.
2029 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2030 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002031
Shawn Pearce69e04d82014-01-29 12:48:54 -08002032 if depth:
2033 current_branch_only = True
2034
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002035 if ID_RE.match(self.revisionExpr) is not None:
2036 is_sha1 = True
2037
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002038 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002039 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002040 # this is a tag and its sha1 value should never change
2041 tag_name = self.revisionExpr[len(R_TAGS):]
2042
2043 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002044 if self._CheckForImmutableRevision():
2045 print('Skipped fetching project %s (already have persistent ref)'
2046 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002047 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002048 if is_sha1 and not depth:
2049 # When syncing a specific commit and --depth is not set:
2050 # * if upstream is explicitly specified and is not a sha1, fetch only
2051 # upstream as users expect only upstream to be fetch.
2052 # Note: The commit might not be in upstream in which case the sync
2053 # will fail.
2054 # * otherwise, fetch all branches to make sure we end up with the
2055 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002056 if self.upstream:
2057 current_branch_only = not ID_RE.match(self.upstream)
2058 else:
2059 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002060
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002061 if not name:
2062 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002063
2064 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002065 remote = self.GetRemote(name)
2066 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002067 ssh_proxy = True
2068
Shawn O. Pearce88443382010-10-08 10:02:09 +02002069 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002070 if alt_dir and 'objects' == os.path.basename(alt_dir):
2071 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002072 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2073 remote = self.GetRemote(name)
2074
David Pursehouse8a68ff92012-09-24 12:15:13 +09002075 all_refs = self.bare_ref.all
2076 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002077 tmp = set()
2078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302079 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002080 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002081 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002082 all_refs[r] = ref_id
2083 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002084 continue
2085
David Pursehouse8a68ff92012-09-24 12:15:13 +09002086 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002087 continue
2088
David Pursehouse8a68ff92012-09-24 12:15:13 +09002089 r = 'refs/_alt/%s' % ref_id
2090 all_refs[r] = ref_id
2091 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002092 tmp.add(r)
2093
heping3d7bbc92017-04-12 19:51:47 +08002094 tmp_packed_lines = []
2095 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302097 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002098 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002099 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002101 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002102
heping3d7bbc92017-04-12 19:51:47 +08002103 tmp_packed = ''.join(tmp_packed_lines)
2104 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002105 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002106 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002107 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002108
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002109 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002110
Conley Owensf97e8382015-01-21 11:12:46 -08002111 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002112 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002113 else:
2114 # If this repo has shallow objects, then we don't know which refs have
2115 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2116 # do this with projects that don't have shallow objects, since it is less
2117 # efficient.
2118 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2119 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002120
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002121 if quiet:
2122 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002123 if not self.worktree:
2124 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002125 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002126
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002127 # If using depth then we should not get all the tags since they may
2128 # be outside of the depth.
2129 if no_tags or depth:
2130 cmd.append('--no-tags')
2131 else:
2132 cmd.append('--tags')
2133
David Pursehouse74cfd272015-10-14 10:50:15 +09002134 if prune:
2135 cmd.append('--prune')
2136
Martin Kellye4e94d22017-03-21 16:05:12 -07002137 if submodules:
2138 cmd.append('--recurse-submodules=on-demand')
2139
Conley Owens80b87fe2014-05-09 17:13:44 -07002140 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002141 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002142 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002143 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002144 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002145 spec.append('tag')
2146 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002147
David Pursehouse403b64e2015-04-27 10:41:33 +09002148 if not self.manifest.IsMirror:
2149 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002150 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002151 # Shallow checkout of a specific commit, fetch from that commit and not
2152 # the heads only as the commit might be deeper in the history.
2153 spec.append(branch)
2154 else:
2155 if is_sha1:
2156 branch = self.upstream
2157 if branch is not None and branch.strip():
2158 if not branch.startswith('refs/'):
2159 branch = R_HEADS + branch
2160 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002161 cmd.extend(spec)
2162
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002163 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002164 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002165 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002166 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002167 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002168 ok = True
2169 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002170 # If needed, run the 'git remote prune' the first time through the loop
2171 elif (not _i and
2172 "error:" in gitcmd.stderr and
2173 "git remote prune" in gitcmd.stderr):
2174 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002175 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002176 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002177 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002178 break
2179 continue
Brian Harring14a66742012-09-28 20:21:57 -07002180 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002181 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2182 # in sha1 mode, we just tried sync'ing from the upstream field; it
2183 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002184 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002185 elif ret < 0:
2186 # Git died with a signal, exit immediately
2187 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002188 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002189
2190 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002191 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002192 if old_packed != '':
2193 _lwrite(packed_refs, old_packed)
2194 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002195 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002196 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002197
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002198 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002199 # We just synced the upstream given branch; verify we
2200 # got what we wanted, else trigger a second run of all
2201 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002202 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002203 if current_branch_only and depth:
2204 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002205 return self._RemoteFetch(name=name,
2206 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002207 initial=False, quiet=quiet, alt_dir=alt_dir,
2208 depth=None)
2209 else:
2210 # Avoid infinite recursion: sync all branches with depth set to None
2211 return self._RemoteFetch(name=name, current_branch_only=False,
2212 initial=False, quiet=quiet, alt_dir=alt_dir,
2213 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002214
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002215 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002216
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002217 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002218 if initial and \
2219 (self.manifest.manifestProject.config.GetString('repo.depth') or
2220 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002221 return False
2222
2223 remote = self.GetRemote(self.remote.name)
2224 bundle_url = remote.url + '/clone.bundle'
2225 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002226 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2227 'persistent-http',
2228 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002229 return False
2230
2231 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2232 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2233
2234 exist_dst = os.path.exists(bundle_dst)
2235 exist_tmp = os.path.exists(bundle_tmp)
2236
2237 if not initial and not exist_dst and not exist_tmp:
2238 return False
2239
2240 if not exist_dst:
2241 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2242 if not exist_dst:
2243 return False
2244
2245 cmd = ['fetch']
2246 if quiet:
2247 cmd.append('--quiet')
2248 if not self.worktree:
2249 cmd.append('--update-head-ok')
2250 cmd.append(bundle_dst)
2251 for f in remote.fetch:
2252 cmd.append(str(f))
2253 cmd.append('refs/tags/*:refs/tags/*')
2254
2255 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002256 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002257 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002258 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002259 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002260 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002261
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002262 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002263 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002264 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002265
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002266 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002267 if quiet:
2268 cmd += ['--silent']
2269 if os.path.exists(tmpPath):
2270 size = os.stat(tmpPath).st_size
2271 if size >= 1024:
2272 cmd += ['--continue-at', '%d' % (size,)]
2273 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002274 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002275 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2276 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002277 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002278 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002279 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002280 if srcUrl.startswith('persistent-'):
2281 srcUrl = srcUrl[len('persistent-'):]
2282 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002283
Dave Borowitz137d0132015-01-02 11:12:54 -08002284 if IsTrace():
2285 Trace('%s', ' '.join(cmd))
2286 try:
2287 proc = subprocess.Popen(cmd)
2288 except OSError:
2289 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002290
Dave Borowitz137d0132015-01-02 11:12:54 -08002291 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002292
Dave Borowitz137d0132015-01-02 11:12:54 -08002293 if curlret == 22:
2294 # From curl man page:
2295 # 22: HTTP page not retrieved. The requested url was not found or
2296 # returned another error with the HTTP error code being 400 or above.
2297 # This return code only appears if -f, --fail is used.
2298 if not quiet:
2299 print("Server does not provide clone.bundle; ignoring.",
2300 file=sys.stderr)
2301 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002302
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002303 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002304 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002305 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002306 return True
2307 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002308 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002309 return False
2310 else:
2311 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002312
Kris Giesingc8d882a2014-12-23 13:02:32 -08002313 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002314 try:
2315 with open(path) as f:
2316 if f.read(16) == '# v2 git bundle\n':
2317 return True
2318 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002319 if not quiet:
2320 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002321 return False
2322 except OSError:
2323 return False
2324
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002325 def _Checkout(self, rev, quiet=False):
2326 cmd = ['checkout']
2327 if quiet:
2328 cmd.append('-q')
2329 cmd.append(rev)
2330 cmd.append('--')
2331 if GitCommand(self, cmd).Wait() != 0:
2332 if self._allrefs:
2333 raise GitError('%s checkout %s ' % (self.name, rev))
2334
Anthony King7bdac712014-07-16 12:56:40 +01002335 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002336 cmd = ['cherry-pick']
2337 cmd.append(rev)
2338 cmd.append('--')
2339 if GitCommand(self, cmd).Wait() != 0:
2340 if self._allrefs:
2341 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2342
Anthony King7bdac712014-07-16 12:56:40 +01002343 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002344 cmd = ['revert']
2345 cmd.append('--no-edit')
2346 cmd.append(rev)
2347 cmd.append('--')
2348 if GitCommand(self, cmd).Wait() != 0:
2349 if self._allrefs:
2350 raise GitError('%s revert %s ' % (self.name, rev))
2351
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002352 def _ResetHard(self, rev, quiet=True):
2353 cmd = ['reset', '--hard']
2354 if quiet:
2355 cmd.append('-q')
2356 cmd.append(rev)
2357 if GitCommand(self, cmd).Wait() != 0:
2358 raise GitError('%s reset --hard %s ' % (self.name, rev))
2359
Martin Kellye4e94d22017-03-21 16:05:12 -07002360 def _SyncSubmodules(self, quiet=True):
2361 cmd = ['submodule', 'update', '--init', '--recursive']
2362 if quiet:
2363 cmd.append('-q')
2364 if GitCommand(self, cmd).Wait() != 0:
2365 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2366
Anthony King7bdac712014-07-16 12:56:40 +01002367 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002368 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002369 if onto is not None:
2370 cmd.extend(['--onto', onto])
2371 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002372 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002373 raise GitError('%s rebase %s ' % (self.name, upstream))
2374
Pierre Tardy3d125942012-05-04 12:18:12 +02002375 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002376 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002377 if ffonly:
2378 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002379 if GitCommand(self, cmd).Wait() != 0:
2380 raise GitError('%s merge %s ' % (self.name, head))
2381
Kevin Degiabaa7f32014-11-12 11:27:45 -07002382 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002383 init_git_dir = not os.path.exists(self.gitdir)
2384 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002385 try:
2386 # Initialize the bare repository, which contains all of the objects.
2387 if init_obj_dir:
2388 os.makedirs(self.objdir)
2389 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002390
Kevin Degib1a07b82015-07-27 13:33:43 -06002391 # If we have a separate directory to hold refs, initialize it as well.
2392 if self.objdir != self.gitdir:
2393 if init_git_dir:
2394 os.makedirs(self.gitdir)
2395
2396 if init_obj_dir or init_git_dir:
2397 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2398 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002399 try:
2400 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2401 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002402 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002403 print("Retrying clone after deleting %s" %
2404 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002405 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002406 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2407 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002408 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002409 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002410 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2411 except:
2412 raise e
2413 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002414
Kevin Degi384b3c52014-10-16 16:02:58 -06002415 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002416 mp = self.manifest.manifestProject
2417 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002418
Kevin Degib1a07b82015-07-27 13:33:43 -06002419 if ref_dir or mirror_git:
2420 if not mirror_git:
2421 mirror_git = os.path.join(ref_dir, self.name + '.git')
2422 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2423 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002424
Kevin Degib1a07b82015-07-27 13:33:43 -06002425 if os.path.exists(mirror_git):
2426 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002427
Kevin Degib1a07b82015-07-27 13:33:43 -06002428 elif os.path.exists(repo_git):
2429 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002430
Kevin Degib1a07b82015-07-27 13:33:43 -06002431 else:
2432 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002433
Kevin Degib1a07b82015-07-27 13:33:43 -06002434 if ref_dir:
2435 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2436 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002437
Kevin Degib1a07b82015-07-27 13:33:43 -06002438 self._UpdateHooks()
2439
2440 m = self.manifest.manifestProject.config
2441 for key in ['user.name', 'user.email']:
2442 if m.Has(key, include_defaults=False):
2443 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002444 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002445 if self.manifest.IsMirror:
2446 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002447 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002448 self.config.SetString('core.bare', None)
2449 except Exception:
2450 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002451 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002452 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002453 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002454 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002455
Jimmie Westera0444582012-10-24 13:44:42 +02002456 def _UpdateHooks(self):
2457 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002458 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002459
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002460 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002461 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002462 if not os.path.exists(hooks):
2463 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002464 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002465 name = os.path.basename(stock_hook)
2466
Victor Boivie65e0f352011-04-18 11:23:29 +02002467 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002468 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002469 # Don't install a Gerrit Code Review hook if this
2470 # project does not appear to use it for reviews.
2471 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002472 # Since the manifest project is one of those, but also
2473 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002474 continue
2475
2476 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002477 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002478 continue
2479 if os.path.exists(dst):
2480 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002481 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002482 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002483 _warn("%s: Not replacing locally modified %s hook",
2484 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002485 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002486 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002487 platform_utils.symlink(
2488 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002489 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002490 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002491 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002492 else:
2493 raise
2494
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002495 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002496 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002497 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002498 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002499 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002500 remote.review = self.remote.review
2501 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002502
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002503 if self.worktree:
2504 remote.ResetFetch(mirror=False)
2505 else:
2506 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002507 remote.Save()
2508
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002509 def _InitMRef(self):
2510 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002511 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002512
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002513 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002514 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002515
2516 def _InitAnyMRef(self, ref):
2517 cur = self.bare_ref.symref(ref)
2518
2519 if self.revisionId:
2520 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2521 msg = 'manifest set to %s' % self.revisionId
2522 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002523 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002524 else:
2525 remote = self.GetRemote(self.remote.name)
2526 dst = remote.ToLocal(self.revisionExpr)
2527 if cur != dst:
2528 msg = 'manifest set to %s' % self.revisionExpr
2529 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002530
Kevin Degi384b3c52014-10-16 16:02:58 -06002531 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002532 symlink_files = self.shareable_files[:]
2533 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002534 if share_refs:
2535 symlink_files += self.working_tree_files
2536 symlink_dirs += self.working_tree_dirs
2537 to_symlink = symlink_files + symlink_dirs
2538 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002539 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002540 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002541 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002542 # Fail if the links are pointing to the wrong place
2543 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002544 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002545 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002546 'work tree. If you\'re comfortable with the '
2547 'possibility of losing the work tree\'s git metadata,'
2548 ' use `repo sync --force-sync {0}` to '
2549 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002550
David James8d201162013-10-11 17:03:19 -07002551 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2552 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2553
2554 Args:
2555 gitdir: The bare git repository. Must already be initialized.
2556 dotgit: The repository you would like to initialize.
2557 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2558 Only one work tree can store refs under a given |gitdir|.
2559 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2560 This saves you the effort of initializing |dotgit| yourself.
2561 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002562 symlink_files = self.shareable_files[:]
2563 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002564 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002565 symlink_files += self.working_tree_files
2566 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002567 to_symlink = symlink_files + symlink_dirs
2568
2569 to_copy = []
2570 if copy_all:
2571 to_copy = os.listdir(gitdir)
2572
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002573 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002574 for name in set(to_copy).union(to_symlink):
2575 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002576 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002577 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002578
Kevin Degi384b3c52014-10-16 16:02:58 -06002579 if os.path.lexists(dst):
2580 continue
David James8d201162013-10-11 17:03:19 -07002581
2582 # If the source dir doesn't exist, create an empty dir.
2583 if name in symlink_dirs and not os.path.lexists(src):
2584 os.makedirs(src)
2585
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002586 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002587 platform_utils.symlink(
2588 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002589 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002590 if os.path.isdir(src):
2591 shutil.copytree(src, dst)
2592 elif os.path.isfile(src):
2593 shutil.copy(src, dst)
2594
Conley Owens80b87fe2014-05-09 17:13:44 -07002595 # If the source file doesn't exist, ensure the destination
2596 # file doesn't either.
2597 if name in symlink_files and not os.path.lexists(src):
2598 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002599 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002600 except OSError:
2601 pass
2602
David James8d201162013-10-11 17:03:19 -07002603 except OSError as e:
2604 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002605 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002606 else:
2607 raise
2608
Martin Kellye4e94d22017-03-21 16:05:12 -07002609 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002610 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002611 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002612 try:
2613 if init_dotgit:
2614 os.makedirs(dotgit)
2615 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2616 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002617
Kevin Degiabaa7f32014-11-12 11:27:45 -07002618 try:
2619 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2620 except GitError as e:
2621 if force_sync:
2622 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002623 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002624 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002625 except:
2626 raise e
2627 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002628
Kevin Degib1a07b82015-07-27 13:33:43 -06002629 if init_dotgit:
2630 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002631
Kevin Degib1a07b82015-07-27 13:33:43 -06002632 cmd = ['read-tree', '--reset', '-u']
2633 cmd.append('-v')
2634 cmd.append(HEAD)
2635 if GitCommand(self, cmd).Wait() != 0:
2636 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002637
Martin Kellye4e94d22017-03-21 16:05:12 -07002638 if submodules:
2639 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002640 self._CopyAndLinkFiles()
2641 except Exception:
2642 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002643 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002644 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002645
Renaud Paquay788e9622017-01-27 11:41:12 -08002646 def _get_symlink_error_message(self):
2647 if platform_utils.isWindows():
2648 return ('Unable to create symbolic link. Please re-run the command as '
2649 'Administrator, or see '
2650 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2651 'for other options.')
2652 return 'filesystem must support symlinks'
2653
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002654 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002655 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002656
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002657 def _revlist(self, *args, **kw):
2658 a = []
2659 a.extend(args)
2660 a.append('--')
2661 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002662
2663 @property
2664 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002665 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002666
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002667 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002668 """Get logs between two revisions of this project."""
2669 comp = '..'
2670 if rev1:
2671 revs = [rev1]
2672 if rev2:
2673 revs.extend([comp, rev2])
2674 cmd = ['log', ''.join(revs)]
2675 out = DiffColoring(self.config)
2676 if out.is_on and color:
2677 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002678 if pretty_format is not None:
2679 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002680 if oneline:
2681 cmd.append('--oneline')
2682
2683 try:
2684 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2685 if log.Wait() == 0:
2686 return log.stdout
2687 except GitError:
2688 # worktree may not exist if groups changed for example. In that case,
2689 # try in gitdir instead.
2690 if not os.path.exists(self.worktree):
2691 return self.bare_git.log(*cmd[1:])
2692 else:
2693 raise
2694 return None
2695
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002696 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2697 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002698 """Get the list of logs from this revision to given revisionId"""
2699 logs = {}
2700 selfId = self.GetRevisionId(self._allrefs)
2701 toId = toProject.GetRevisionId(toProject._allrefs)
2702
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002703 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2704 pretty_format=pretty_format)
2705 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2706 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002707 return logs
2708
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002710
David James8d201162013-10-11 17:03:19 -07002711 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002712 self._project = project
2713 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002714 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002715
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002716 def LsOthers(self):
2717 p = GitCommand(self._project,
2718 ['ls-files',
2719 '-z',
2720 '--others',
2721 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002722 bare=False,
David James8d201162013-10-11 17:03:19 -07002723 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002724 capture_stdout=True,
2725 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002726 if p.Wait() == 0:
2727 out = p.stdout
2728 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002729 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002730 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002731 return []
2732
2733 def DiffZ(self, name, *args):
2734 cmd = [name]
2735 cmd.append('-z')
2736 cmd.extend(args)
2737 p = GitCommand(self._project,
2738 cmd,
David James8d201162013-10-11 17:03:19 -07002739 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002740 bare=False,
2741 capture_stdout=True,
2742 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002743 try:
2744 out = p.process.stdout.read()
2745 r = {}
2746 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002747 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002748 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002749 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002750 info = next(out)
2751 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002752 except StopIteration:
2753 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002754
2755 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002756
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002757 def __init__(self, path, omode, nmode, oid, nid, state):
2758 self.path = path
2759 self.src_path = None
2760 self.old_mode = omode
2761 self.new_mode = nmode
2762 self.old_id = oid
2763 self.new_id = nid
2764
2765 if len(state) == 1:
2766 self.status = state
2767 self.level = None
2768 else:
2769 self.status = state[:1]
2770 self.level = state[1:]
2771 while self.level.startswith('0'):
2772 self.level = self.level[1:]
2773
2774 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002775 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002776 if info.status in ('R', 'C'):
2777 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002778 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779 r[info.path] = info
2780 return r
2781 finally:
2782 p.Wait()
2783
2784 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002785 if self._bare:
2786 path = os.path.join(self._project.gitdir, HEAD)
2787 else:
2788 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002789 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002790 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002791 except IOError as e:
2792 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002793 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002794 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002795 finally:
2796 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302797 try:
2798 line = line.decode()
2799 except AttributeError:
2800 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002801 if line.startswith('ref: '):
2802 return line[5:-1]
2803 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002804
2805 def SetHead(self, ref, message=None):
2806 cmdv = []
2807 if message is not None:
2808 cmdv.extend(['-m', message])
2809 cmdv.append(HEAD)
2810 cmdv.append(ref)
2811 self.symbolic_ref(*cmdv)
2812
2813 def DetachHead(self, new, message=None):
2814 cmdv = ['--no-deref']
2815 if message is not None:
2816 cmdv.extend(['-m', message])
2817 cmdv.append(HEAD)
2818 cmdv.append(new)
2819 self.update_ref(*cmdv)
2820
2821 def UpdateRef(self, name, new, old=None,
2822 message=None,
2823 detach=False):
2824 cmdv = []
2825 if message is not None:
2826 cmdv.extend(['-m', message])
2827 if detach:
2828 cmdv.append('--no-deref')
2829 cmdv.append(name)
2830 cmdv.append(new)
2831 if old is not None:
2832 cmdv.append(old)
2833 self.update_ref(*cmdv)
2834
2835 def DeleteRef(self, name, old=None):
2836 if not old:
2837 old = self.rev_parse(name)
2838 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002839 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002841 def rev_list(self, *args, **kw):
2842 if 'format' in kw:
2843 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2844 else:
2845 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002846 cmdv.extend(args)
2847 p = GitCommand(self._project,
2848 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002849 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002850 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002851 capture_stdout=True,
2852 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002853 r = []
2854 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002855 if line[-1] == '\n':
2856 line = line[:-1]
2857 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002858 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002859 raise GitError('%s rev-list %s: %s' %
2860 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002861 return r
2862
2863 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002864 """Allow arbitrary git commands using pythonic syntax.
2865
2866 This allows you to do things like:
2867 git_obj.rev_parse('HEAD')
2868
2869 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2870 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002871 Any other positional arguments will be passed to the git command, and the
2872 following keyword arguments are supported:
2873 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002874
2875 Args:
2876 name: The name of the git command to call. Any '_' characters will
2877 be replaced with '-'.
2878
2879 Returns:
2880 A callable object that will try to call git with the named command.
2881 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002882 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002883
Dave Borowitz091f8932012-10-23 17:01:04 -07002884 def runner(*args, **kwargs):
2885 cmdv = []
2886 config = kwargs.pop('config', None)
2887 for k in kwargs:
2888 raise TypeError('%s() got an unexpected keyword argument %r'
2889 % (name, k))
2890 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002891 if not git_require((1, 7, 2)):
2892 raise ValueError('cannot set config on command line for %s()'
2893 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302894 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002895 cmdv.append('-c')
2896 cmdv.append('%s=%s' % (k, v))
2897 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002898 cmdv.extend(args)
2899 p = GitCommand(self._project,
2900 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002901 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002902 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002903 capture_stdout=True,
2904 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002905 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002906 raise GitError('%s %s: %s' %
2907 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002908 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302909 try:
Conley Owensedd01512013-09-26 12:59:58 -07002910 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302911 except AttributeError:
2912 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002913 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2914 return r[:-1]
2915 return r
2916 return runner
2917
2918
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002919class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002920
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002921 def __str__(self):
2922 return 'prior sync failed; rebase still in progress'
2923
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002924
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002925class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002926
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002927 def __str__(self):
2928 return 'contains uncommitted changes'
2929
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002930
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002931class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002932
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002933 def __init__(self, project, text):
2934 self.project = project
2935 self.text = text
2936
2937 def Print(self, syncbuf):
2938 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2939 syncbuf.out.nl()
2940
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002941
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002942class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002943
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002944 def __init__(self, project, why):
2945 self.project = project
2946 self.why = why
2947
2948 def Print(self, syncbuf):
2949 syncbuf.out.fail('error: %s/: %s',
2950 self.project.relpath,
2951 str(self.why))
2952 syncbuf.out.nl()
2953
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002954
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002955class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002956
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002957 def __init__(self, project, action):
2958 self.project = project
2959 self.action = action
2960
2961 def Run(self, syncbuf):
2962 out = syncbuf.out
2963 out.project('project %s/', self.project.relpath)
2964 out.nl()
2965 try:
2966 self.action()
2967 out.nl()
2968 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002969 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002970 out.nl()
2971 return False
2972
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002973
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002974class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002975
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002976 def __init__(self, config):
2977 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002978 self.project = self.printer('header', attr='bold')
2979 self.info = self.printer('info')
2980 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002981
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002982
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002983class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002984
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002985 def __init__(self, config, detach_head=False):
2986 self._messages = []
2987 self._failures = []
2988 self._later_queue1 = []
2989 self._later_queue2 = []
2990
2991 self.out = _SyncColoring(config)
2992 self.out.redirect(sys.stderr)
2993
2994 self.detach_head = detach_head
2995 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002996 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002997
2998 def info(self, project, fmt, *args):
2999 self._messages.append(_InfoMessage(project, fmt % args))
3000
3001 def fail(self, project, err=None):
3002 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003003 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004
3005 def later1(self, project, what):
3006 self._later_queue1.append(_Later(project, what))
3007
3008 def later2(self, project, what):
3009 self._later_queue2.append(_Later(project, what))
3010
3011 def Finish(self):
3012 self._PrintMessages()
3013 self._RunLater()
3014 self._PrintMessages()
3015 return self.clean
3016
David Rileye0684ad2017-04-05 00:02:59 -07003017 def Recently(self):
3018 recent_clean = self.recent_clean
3019 self.recent_clean = True
3020 return recent_clean
3021
3022 def _MarkUnclean(self):
3023 self.clean = False
3024 self.recent_clean = False
3025
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003026 def _RunLater(self):
3027 for q in ['_later_queue1', '_later_queue2']:
3028 if not self._RunQueue(q):
3029 return
3030
3031 def _RunQueue(self, queue):
3032 for m in getattr(self, queue):
3033 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003034 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003035 return False
3036 setattr(self, queue, [])
3037 return True
3038
3039 def _PrintMessages(self):
3040 for m in self._messages:
3041 m.Print(self)
3042 for m in self._failures:
3043 m.Print(self)
3044
3045 self._messages = []
3046 self._failures = []
3047
3048
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003049class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003050
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003051 """A special project housed under .repo.
3052 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003053
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003054 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003055 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003056 manifest=manifest,
3057 name=name,
3058 gitdir=gitdir,
3059 objdir=gitdir,
3060 worktree=worktree,
3061 remote=RemoteSpec('origin'),
3062 relpath='.repo/%s' % name,
3063 revisionExpr='refs/heads/master',
3064 revisionId=None,
3065 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003066
3067 def PreSync(self):
3068 if self.Exists:
3069 cb = self.CurrentBranch
3070 if cb:
3071 base = self.GetBranch(cb).merge
3072 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003073 self.revisionExpr = base
3074 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003075
Martin Kelly224a31a2017-07-10 14:46:25 -07003076 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003077 """ Prepare MetaProject for manifest branch switch
3078 """
3079
3080 # detach and delete manifest branch, allowing a new
3081 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003082 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003083 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003084 syncbuf.Finish()
3085
3086 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003087 ['update-ref', '-d', 'refs/heads/default'],
3088 capture_stdout=True,
3089 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003090
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003091 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003092 def LastFetch(self):
3093 try:
3094 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3095 return os.path.getmtime(fh)
3096 except OSError:
3097 return 0
3098
3099 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003100 def HasChanges(self):
3101 """Has the remote received new commits not yet checked out?
3102 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003103 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003104 return False
3105
David Pursehouse8a68ff92012-09-24 12:15:13 +09003106 all_refs = self.bare_ref.all
3107 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003108 head = self.work_git.GetHead()
3109 if head.startswith(R_HEADS):
3110 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003111 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003112 except KeyError:
3113 head = None
3114
3115 if revid == head:
3116 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003117 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003118 return True
3119 return False