blob: 24816d0f0100dd7c29fe05891df7f4c7c410524a [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://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001155 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001156
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001157 for push_option in (push_options or []):
1158 cmd.append('-o')
1159 cmd.append(push_option)
1160
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001161 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001162
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001163 if dest_branch.startswith(R_HEADS):
1164 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001165
1166 upload_type = 'for'
1167 if draft:
1168 upload_type = 'drafts'
1169
1170 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1171 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001172 if auto_topic:
1173 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001174
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001175 opts = ['r=%s' % p for p in people[0]]
1176 opts += ['cc=%s' % p for p in people[1]]
1177 if private:
1178 opts += ['private']
1179 if wip:
1180 opts += ['wip']
1181 if opts:
1182 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001183 cmd.append(ref_spec)
1184
Anthony King7bdac712014-07-16 12:56:40 +01001185 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001186 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
1188 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1189 self.bare_git.UpdateRef(R_PUB + branch.name,
1190 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001191 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192
1193
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001194# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195
Julien Campergue335f5ef2013-10-16 11:02:35 +02001196 def _ExtractArchive(self, tarpath, path=None):
1197 """Extract the given tar on its current location
1198
1199 Args:
1200 - tarpath: The path to the actual tar file
1201
1202 """
1203 try:
1204 with tarfile.open(tarpath, 'r') as tar:
1205 tar.extractall(path=path)
1206 return True
1207 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001208 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001209 return False
1210
Ningning Xiac2fbc782016-08-22 14:24:39 -07001211 def CachePopulate(self, cache_dir, url):
1212 """Populate cache in the cache_dir.
1213
1214 Args:
1215 cache_dir: Directory to cache git files from Google Storage.
1216 url: Git url of current repository.
1217
1218 Raises:
1219 CacheApplyError if it fails to populate the git cache.
1220 """
1221 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1222 '--cache-dir', cache_dir, url]
1223
1224 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1225 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1226 'url: %s' % (cache_dir, url))
1227
1228 def CacheExists(self, cache_dir, url):
1229 """Check the existence of the cache files.
1230
1231 Args:
1232 cache_dir: Directory to cache git files.
1233 url: Git url of current repository.
1234
1235 Raises:
1236 CacheApplyError if the cache files do not exist.
1237 """
1238 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1239
1240 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1241 if exist.Wait() != 0:
1242 raise CacheApplyError('Failed to execute git cache exists cmd. '
1243 'cache_dir: %s url: %s' % (cache_dir, url))
1244
1245 if not exist.stdout or not exist.stdout.strip():
1246 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1247 'url: %s' % (cache_dir, url))
1248 return exist.stdout.strip()
1249
1250 def CacheApply(self, cache_dir):
1251 """Apply git cache files populated from Google Storage buckets.
1252
1253 Args:
1254 cache_dir: Directory to cache git files.
1255
1256 Raises:
1257 CacheApplyError if it fails to apply git caches.
1258 """
1259 remote = self.GetRemote(self.remote.name)
1260
1261 self.CachePopulate(cache_dir, remote.url)
1262
1263 mirror_dir = self.CacheExists(cache_dir, remote.url)
1264
1265 refspec = RefSpec(True, 'refs/heads/*',
1266 'refs/remotes/%s/*' % remote.name)
1267
1268 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1269 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1270 raise CacheApplyError('Failed to fetch refs %s from %s' %
1271 (mirror_dir, str(refspec)))
1272
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001273 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001274 quiet=False,
1275 is_new=None,
1276 current_branch_only=False,
1277 force_sync=False,
1278 clone_bundle=True,
1279 no_tags=False,
1280 archive=False,
1281 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001282 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001283 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001284 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001285 """Perform only the network IO portion of the sync process.
1286 Local working directory/branch state is not affected.
1287 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001288 if archive and not isinstance(self, MetaProject):
1289 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001290 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001291 return False
1292
1293 name = self.relpath.replace('\\', '/')
1294 name = name.replace('/', '_')
1295 tarpath = '%s.tar' % name
1296 topdir = self.manifest.topdir
1297
1298 try:
1299 self._FetchArchive(tarpath, cwd=topdir)
1300 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001301 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001302 return False
1303
1304 # From now on, we only need absolute tarpath
1305 tarpath = os.path.join(topdir, tarpath)
1306
1307 if not self._ExtractArchive(tarpath, path=topdir):
1308 return False
1309 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001310 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001311 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001312 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001313 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001314 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001315 if is_new is None:
1316 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001317 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001318 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001319 else:
1320 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001321 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001322
1323 if is_new:
1324 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1325 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001326 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001327 try:
1328 alt_dir = fd.readline().rstrip()
1329 finally:
1330 fd.close()
1331 except IOError:
1332 alt_dir = None
1333 else:
1334 alt_dir = None
1335
Ningning Xiac2fbc782016-08-22 14:24:39 -07001336 applied_cache = False
1337 # If cache_dir is provided, and it's a new repository without
1338 # alternative_dir, bootstrap this project repo with the git
1339 # cache files.
1340 if cache_dir is not None and is_new and alt_dir is None:
1341 try:
1342 self.CacheApply(cache_dir)
1343 applied_cache = True
1344 is_new = False
1345 except CacheApplyError as e:
1346 _error('Could not apply git cache: %s', e)
1347 _error('Please check if you have the right GS credentials.')
1348 _error('Please check if the cache files exist in GS.')
1349
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001350 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001351 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001352 and alt_dir is None \
1353 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001354 is_new = False
1355
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001356 if not current_branch_only:
1357 if self.sync_c:
1358 current_branch_only = True
1359 elif not self.manifest._loaded:
1360 # Manifest cannot check defaults until it syncs.
1361 current_branch_only = False
1362 elif self.manifest.default.sync_c:
1363 current_branch_only = True
1364
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001365 if self.clone_depth:
1366 depth = self.clone_depth
1367 else:
1368 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1369
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001370 need_to_fetch = not (optimized_fetch and
1371 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001372 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001373 if (need_to_fetch and
1374 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1375 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001376 no_tags=no_tags, prune=prune, depth=depth,
1377 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001378 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001379
1380 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001381 self._InitMRef()
1382 else:
1383 self._InitMirrorHead()
1384 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001385 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001386 except OSError:
1387 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001388 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001389
1390 def PostRepoUpgrade(self):
1391 self._InitHooks()
1392
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001393 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001394 if self.manifest.isGitcClient:
1395 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001396 for copyfile in self.copyfiles:
1397 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001398 for linkfile in self.linkfiles:
1399 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001400
Julien Camperguedd654222014-01-09 16:21:37 +01001401 def GetCommitRevisionId(self):
1402 """Get revisionId of a commit.
1403
1404 Use this method instead of GetRevisionId to get the id of the commit rather
1405 than the id of the current git object (for example, a tag)
1406
1407 """
1408 if not self.revisionExpr.startswith(R_TAGS):
1409 return self.GetRevisionId(self._allrefs)
1410
1411 try:
1412 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1413 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001414 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1415 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001416
David Pursehouse8a68ff92012-09-24 12:15:13 +09001417 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001418 if self.revisionId:
1419 return self.revisionId
1420
1421 rem = self.GetRemote(self.remote.name)
1422 rev = rem.ToLocal(self.revisionExpr)
1423
David Pursehouse8a68ff92012-09-24 12:15:13 +09001424 if all_refs is not None and rev in all_refs:
1425 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001426
1427 try:
1428 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1429 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001430 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1431 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001432
Martin Kellye4e94d22017-03-21 16:05:12 -07001433 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434 """Perform only the local IO portion of the sync process.
1435 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001436 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001437 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001438 all_refs = self.bare_ref.all
1439 self.CleanPublishedCache(all_refs)
1440 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001441
David Pursehouse1d947b32012-10-25 12:23:11 +09001442 def _doff():
1443 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001444 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001445
Martin Kellye4e94d22017-03-21 16:05:12 -07001446 def _dosubmodules():
1447 self._SyncSubmodules(quiet=True)
1448
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001449 head = self.work_git.GetHead()
1450 if head.startswith(R_HEADS):
1451 branch = head[len(R_HEADS):]
1452 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001453 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001454 except KeyError:
1455 head = None
1456 else:
1457 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001458
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001459 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001460 # Currently on a detached HEAD. The user is assumed to
1461 # not have any local modifications worth worrying about.
1462 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001463 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001464 syncbuf.fail(self, _PriorSyncFailedError())
1465 return
1466
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001467 if head == revid:
1468 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001469 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001470 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001471 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001472 # The copy/linkfile config may have changed.
1473 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001474 return
1475 else:
1476 lost = self._revlist(not_rev(revid), HEAD)
1477 if lost:
1478 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001479
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001480 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001481 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001482 if submodules:
1483 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001484 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001485 syncbuf.fail(self, e)
1486 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001487 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001488 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001489
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001490 if head == revid:
1491 # No changes; don't do anything further.
1492 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001493 # The copy/linkfile config may have changed.
1494 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001495 return
1496
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001497 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001498
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001499 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001500 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001501 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001503 syncbuf.info(self,
1504 "leaving %s; does not track upstream",
1505 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001507 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001508 if submodules:
1509 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001510 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001511 syncbuf.fail(self, e)
1512 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001513 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001514 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001516 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001517 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001518 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001519 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001520 if not_merged:
1521 if upstream_gain:
1522 # The user has published this branch and some of those
1523 # commits are not yet merged upstream. We do not want
1524 # to rewrite the published commits so we punt.
1525 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001526 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001527 "branch %s is published (but not merged) and is now "
1528 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001529 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001530 elif pub == head:
1531 # All published commits are merged, and thus we are a
1532 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001533 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001534 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001535 if submodules:
1536 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001537 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001538
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001539 # Examine the local commits not in the remote. Find the
1540 # last one attributed to this user, if any.
1541 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001542 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001543 last_mine = None
1544 cnt_mine = 0
1545 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301546 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001547 if committer_email == self.UserEmail:
1548 last_mine = commit_id
1549 cnt_mine += 1
1550
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001551 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001552 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
1554 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001555 syncbuf.fail(self, _DirtyError())
1556 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001558 # If the upstream switched on us, warn the user.
1559 #
1560 if branch.merge != self.revisionExpr:
1561 if branch.merge and self.revisionExpr:
1562 syncbuf.info(self,
1563 'manifest switched %s...%s',
1564 branch.merge,
1565 self.revisionExpr)
1566 elif branch.merge:
1567 syncbuf.info(self,
1568 'manifest no longer tracks %s',
1569 branch.merge)
1570
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001571 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001572 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001573 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001574 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001575 syncbuf.info(self,
1576 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001577 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001578
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001579 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001580 if not ID_RE.match(self.revisionExpr):
1581 # in case of manifest sync the revisionExpr might be a SHA1
1582 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001583 if not branch.merge.startswith('refs/'):
1584 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001585 branch.Save()
1586
Mike Pontillod3153822012-02-28 11:53:24 -08001587 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001588 def _docopyandlink():
1589 self._CopyAndLinkFiles()
1590
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001591 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001592 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001593 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001594 if submodules:
1595 syncbuf.later2(self, _dosubmodules)
1596 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001597 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001598 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001599 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001600 if submodules:
1601 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001602 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001603 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001604 syncbuf.fail(self, e)
1605 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001606 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001607 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001608 if submodules:
1609 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001610
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001611 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001612 # dest should already be an absolute path, but src is project relative
1613 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001614 abssrc = os.path.join(self.worktree, src)
1615 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001616
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001617 def AddLinkFile(self, src, dest, absdest):
1618 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001619 # make src relative path to dest
1620 absdestdir = os.path.dirname(absdest)
1621 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001622 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001623
James W. Mills24c13082012-04-12 15:04:13 -05001624 def AddAnnotation(self, name, value, keep):
1625 self.annotations.append(_Annotation(name, value, keep))
1626
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001627 def DownloadPatchSet(self, change_id, patch_id):
1628 """Download a single patch set of a single change to FETCH_HEAD.
1629 """
1630 remote = self.GetRemote(self.remote.name)
1631
1632 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001633 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001634 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001635 if GitCommand(self, cmd, bare=True).Wait() != 0:
1636 return None
1637 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001638 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001639 change_id,
1640 patch_id,
1641 self.bare_git.rev_parse('FETCH_HEAD'))
1642
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001643
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001644# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001645
Simran Basib9a1b732015-08-20 12:19:28 -07001646 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001647 """Create a new branch off the manifest's revision.
1648 """
Simran Basib9a1b732015-08-20 12:19:28 -07001649 if not branch_merge:
1650 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001651 head = self.work_git.GetHead()
1652 if head == (R_HEADS + name):
1653 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001654
David Pursehouse8a68ff92012-09-24 12:15:13 +09001655 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001656 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001657 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001658 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001659 capture_stdout=True,
1660 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001661
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001662 branch = self.GetBranch(name)
1663 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001664 branch.merge = branch_merge
1665 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1666 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001667 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001668
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001669 if head.startswith(R_HEADS):
1670 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001671 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001672 except KeyError:
1673 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001674 if revid and head and revid == head:
1675 ref = os.path.join(self.gitdir, R_HEADS + name)
1676 try:
1677 os.makedirs(os.path.dirname(ref))
1678 except OSError:
1679 pass
1680 _lwrite(ref, '%s\n' % revid)
1681 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1682 'ref: %s%s\n' % (R_HEADS, name))
1683 branch.Save()
1684 return True
1685
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001686 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001687 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001688 capture_stdout=True,
1689 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001690 branch.Save()
1691 return True
1692 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001693
Wink Saville02d79452009-04-10 13:01:24 -07001694 def CheckoutBranch(self, name):
1695 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001696
1697 Args:
1698 name: The name of the branch to checkout.
1699
1700 Returns:
1701 True if the checkout succeeded; False if it didn't; None if the branch
1702 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001703 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001704 rev = R_HEADS + name
1705 head = self.work_git.GetHead()
1706 if head == rev:
1707 # Already on the branch
1708 #
1709 return True
Wink Saville02d79452009-04-10 13:01:24 -07001710
David Pursehouse8a68ff92012-09-24 12:15:13 +09001711 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001712 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001713 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001714 except KeyError:
1715 # Branch does not exist in this project
1716 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001717 return None
Wink Saville02d79452009-04-10 13:01:24 -07001718
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001719 if head.startswith(R_HEADS):
1720 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001721 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001722 except KeyError:
1723 head = None
1724
1725 if head == revid:
1726 # Same revision; just update HEAD to point to the new
1727 # target branch, but otherwise take no other action.
1728 #
1729 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1730 'ref: %s%s\n' % (R_HEADS, name))
1731 return True
1732
1733 return GitCommand(self,
1734 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001735 capture_stdout=True,
1736 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001737
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001738 def AbandonBranch(self, name):
1739 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001740
1741 Args:
1742 name: The name of the branch to abandon.
1743
1744 Returns:
1745 True if the abandon succeeded; False if it didn't; None if the branch
1746 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001747 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001748 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001749 all_refs = self.bare_ref.all
1750 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001751 # Doesn't exist
1752 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001753
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001754 head = self.work_git.GetHead()
1755 if head == rev:
1756 # We can't destroy the branch while we are sitting
1757 # on it. Switch to a detached HEAD.
1758 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001759 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001760
David Pursehouse8a68ff92012-09-24 12:15:13 +09001761 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001762 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001763 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1764 '%s\n' % revid)
1765 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001766 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001767
1768 return GitCommand(self,
1769 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001770 capture_stdout=True,
1771 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001772
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001773 def PruneHeads(self):
1774 """Prune any topic branches already merged into upstream.
1775 """
1776 cb = self.CurrentBranch
1777 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001778 left = self._allrefs
1779 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001780 if name.startswith(R_HEADS):
1781 name = name[len(R_HEADS):]
1782 if cb is None or name != cb:
1783 kill.append(name)
1784
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001785 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001786 if cb is not None \
1787 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001788 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001789 self.work_git.DetachHead(HEAD)
1790 kill.append(cb)
1791
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001792 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001793 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001794
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001795 try:
1796 self.bare_git.DetachHead(rev)
1797
1798 b = ['branch', '-d']
1799 b.extend(kill)
1800 b = GitCommand(self, b, bare=True,
1801 capture_stdout=True,
1802 capture_stderr=True)
1803 b.Wait()
1804 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001805 if ID_RE.match(old):
1806 self.bare_git.DetachHead(old)
1807 else:
1808 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001809 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001810
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001811 for branch in kill:
1812 if (R_HEADS + branch) not in left:
1813 self.CleanPublishedCache()
1814 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001815
1816 if cb and cb not in kill:
1817 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001818 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001819
1820 kept = []
1821 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001822 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001823 branch = self.GetBranch(branch)
1824 base = branch.LocalMerge
1825 if not base:
1826 base = rev
1827 kept.append(ReviewableBranch(self, branch, base))
1828 return kept
1829
1830
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001831# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001832
1833 def GetRegisteredSubprojects(self):
1834 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001835
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001836 def rec(subprojects):
1837 if not subprojects:
1838 return
1839 result.extend(subprojects)
1840 for p in subprojects:
1841 rec(p.subprojects)
1842 rec(self.subprojects)
1843 return result
1844
1845 def _GetSubmodules(self):
1846 # Unfortunately we cannot call `git submodule status --recursive` here
1847 # because the working tree might not exist yet, and it cannot be used
1848 # without a working tree in its current implementation.
1849
1850 def get_submodules(gitdir, rev):
1851 # Parse .gitmodules for submodule sub_paths and sub_urls
1852 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1853 if not sub_paths:
1854 return []
1855 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1856 # revision of submodule repository
1857 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1858 submodules = []
1859 for sub_path, sub_url in zip(sub_paths, sub_urls):
1860 try:
1861 sub_rev = sub_revs[sub_path]
1862 except KeyError:
1863 # Ignore non-exist submodules
1864 continue
1865 submodules.append((sub_rev, sub_path, sub_url))
1866 return submodules
1867
1868 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1869 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001870
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001871 def parse_gitmodules(gitdir, rev):
1872 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1873 try:
Anthony King7bdac712014-07-16 12:56:40 +01001874 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1875 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001876 except GitError:
1877 return [], []
1878 if p.Wait() != 0:
1879 return [], []
1880
1881 gitmodules_lines = []
1882 fd, temp_gitmodules_path = tempfile.mkstemp()
1883 try:
1884 os.write(fd, p.stdout)
1885 os.close(fd)
1886 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001887 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1888 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001889 if p.Wait() != 0:
1890 return [], []
1891 gitmodules_lines = p.stdout.split('\n')
1892 except GitError:
1893 return [], []
1894 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001895 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001896
1897 names = set()
1898 paths = {}
1899 urls = {}
1900 for line in gitmodules_lines:
1901 if not line:
1902 continue
1903 m = re_path.match(line)
1904 if m:
1905 names.add(m.group(1))
1906 paths[m.group(1)] = m.group(2)
1907 continue
1908 m = re_url.match(line)
1909 if m:
1910 names.add(m.group(1))
1911 urls[m.group(1)] = m.group(2)
1912 continue
1913 names = sorted(names)
1914 return ([paths.get(name, '') for name in names],
1915 [urls.get(name, '') for name in names])
1916
1917 def git_ls_tree(gitdir, rev, paths):
1918 cmd = ['ls-tree', rev, '--']
1919 cmd.extend(paths)
1920 try:
Anthony King7bdac712014-07-16 12:56:40 +01001921 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1922 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001923 except GitError:
1924 return []
1925 if p.Wait() != 0:
1926 return []
1927 objects = {}
1928 for line in p.stdout.split('\n'):
1929 if not line.strip():
1930 continue
1931 object_rev, object_path = line.split()[2:4]
1932 objects[object_path] = object_rev
1933 return objects
1934
1935 try:
1936 rev = self.GetRevisionId()
1937 except GitError:
1938 return []
1939 return get_submodules(self.gitdir, rev)
1940
1941 def GetDerivedSubprojects(self):
1942 result = []
1943 if not self.Exists:
1944 # If git repo does not exist yet, querying its submodules will
1945 # mess up its states; so return here.
1946 return result
1947 for rev, path, url in self._GetSubmodules():
1948 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001949 relpath, worktree, gitdir, objdir = \
1950 self.manifest.GetSubprojectPaths(self, name, path)
1951 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001952 if project:
1953 result.extend(project.GetDerivedSubprojects())
1954 continue
David James8d201162013-10-11 17:03:19 -07001955
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001956 if url.startswith('..'):
1957 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001958 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001959 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001960 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001961 review=self.remote.review,
1962 revision=self.remote.revision)
1963 subproject = Project(manifest=self.manifest,
1964 name=name,
1965 remote=remote,
1966 gitdir=gitdir,
1967 objdir=objdir,
1968 worktree=worktree,
1969 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001970 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001971 revisionId=rev,
1972 rebase=self.rebase,
1973 groups=self.groups,
1974 sync_c=self.sync_c,
1975 sync_s=self.sync_s,
1976 parent=self,
1977 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001978 result.append(subproject)
1979 result.extend(subproject.GetDerivedSubprojects())
1980 return result
1981
1982
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001983# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001984 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001985 try:
1986 # if revision (sha or tag) is not present then following function
1987 # throws an error.
1988 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1989 return True
1990 except GitError:
1991 # There is no such persistent revision. We have to fetch it.
1992 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001993
Julien Campergue335f5ef2013-10-16 11:02:35 +02001994 def _FetchArchive(self, tarpath, cwd=None):
1995 cmd = ['archive', '-v', '-o', tarpath]
1996 cmd.append('--remote=%s' % self.remote.url)
1997 cmd.append('--prefix=%s/' % self.relpath)
1998 cmd.append(self.revisionExpr)
1999
2000 command = GitCommand(self, cmd, cwd=cwd,
2001 capture_stdout=True,
2002 capture_stderr=True)
2003
2004 if command.Wait() != 0:
2005 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2006
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002007 def _RemoteFetch(self, name=None,
2008 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002009 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002010 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002011 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002012 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002013 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002014 depth=None,
2015 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002016
2017 is_sha1 = False
2018 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002019 # The depth should not be used when fetching to a mirror because
2020 # it will result in a shallow repository that cannot be cloned or
2021 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002022 # The repo project should also never be synced with partial depth.
2023 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2024 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002025
Shawn Pearce69e04d82014-01-29 12:48:54 -08002026 if depth:
2027 current_branch_only = True
2028
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002029 if ID_RE.match(self.revisionExpr) is not None:
2030 is_sha1 = True
2031
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002032 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002033 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002034 # this is a tag and its sha1 value should never change
2035 tag_name = self.revisionExpr[len(R_TAGS):]
2036
2037 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002038 if self._CheckForImmutableRevision():
2039 print('Skipped fetching project %s (already have persistent ref)'
2040 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002041 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002042 if is_sha1 and not depth:
2043 # When syncing a specific commit and --depth is not set:
2044 # * if upstream is explicitly specified and is not a sha1, fetch only
2045 # upstream as users expect only upstream to be fetch.
2046 # Note: The commit might not be in upstream in which case the sync
2047 # will fail.
2048 # * otherwise, fetch all branches to make sure we end up with the
2049 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002050 if self.upstream:
2051 current_branch_only = not ID_RE.match(self.upstream)
2052 else:
2053 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002054
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002055 if not name:
2056 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002057
2058 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002059 remote = self.GetRemote(name)
2060 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002061 ssh_proxy = True
2062
Shawn O. Pearce88443382010-10-08 10:02:09 +02002063 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002064 if alt_dir and 'objects' == os.path.basename(alt_dir):
2065 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002066 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2067 remote = self.GetRemote(name)
2068
David Pursehouse8a68ff92012-09-24 12:15:13 +09002069 all_refs = self.bare_ref.all
2070 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002071 tmp = set()
2072
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302073 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002074 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002075 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002076 all_refs[r] = ref_id
2077 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002078 continue
2079
David Pursehouse8a68ff92012-09-24 12:15:13 +09002080 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002081 continue
2082
David Pursehouse8a68ff92012-09-24 12:15:13 +09002083 r = 'refs/_alt/%s' % ref_id
2084 all_refs[r] = ref_id
2085 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002086 tmp.add(r)
2087
heping3d7bbc92017-04-12 19:51:47 +08002088 tmp_packed_lines = []
2089 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002090
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302091 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002092 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002093 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002094 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002095 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096
heping3d7bbc92017-04-12 19:51:47 +08002097 tmp_packed = ''.join(tmp_packed_lines)
2098 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002099 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002100 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002101 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002102
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002103 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002104
Conley Owensf97e8382015-01-21 11:12:46 -08002105 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002106 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002107 else:
2108 # If this repo has shallow objects, then we don't know which refs have
2109 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2110 # do this with projects that don't have shallow objects, since it is less
2111 # efficient.
2112 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2113 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002114
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002115 if quiet:
2116 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002117 if not self.worktree:
2118 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002119 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002120
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002121 # If using depth then we should not get all the tags since they may
2122 # be outside of the depth.
2123 if no_tags or depth:
2124 cmd.append('--no-tags')
2125 else:
2126 cmd.append('--tags')
2127
David Pursehouse74cfd272015-10-14 10:50:15 +09002128 if prune:
2129 cmd.append('--prune')
2130
Martin Kellye4e94d22017-03-21 16:05:12 -07002131 if submodules:
2132 cmd.append('--recurse-submodules=on-demand')
2133
Conley Owens80b87fe2014-05-09 17:13:44 -07002134 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002135 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002136 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002137 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002138 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002139 spec.append('tag')
2140 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002141
David Pursehouse403b64e2015-04-27 10:41:33 +09002142 if not self.manifest.IsMirror:
2143 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002144 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002145 # Shallow checkout of a specific commit, fetch from that commit and not
2146 # the heads only as the commit might be deeper in the history.
2147 spec.append(branch)
2148 else:
2149 if is_sha1:
2150 branch = self.upstream
2151 if branch is not None and branch.strip():
2152 if not branch.startswith('refs/'):
2153 branch = R_HEADS + branch
2154 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002155 cmd.extend(spec)
2156
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002157 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002158 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002159 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002160 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002161 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002162 ok = True
2163 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002164 # If needed, run the 'git remote prune' the first time through the loop
2165 elif (not _i and
2166 "error:" in gitcmd.stderr and
2167 "git remote prune" in gitcmd.stderr):
2168 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002169 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002170 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002171 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002172 break
2173 continue
Brian Harring14a66742012-09-28 20:21:57 -07002174 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002175 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2176 # in sha1 mode, we just tried sync'ing from the upstream field; it
2177 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002178 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002179 elif ret < 0:
2180 # Git died with a signal, exit immediately
2181 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002182 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002183
2184 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002185 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002186 if old_packed != '':
2187 _lwrite(packed_refs, old_packed)
2188 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002189 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002190 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002191
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002192 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002193 # We just synced the upstream given branch; verify we
2194 # got what we wanted, else trigger a second run of all
2195 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002196 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002197 if current_branch_only and depth:
2198 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002199 return self._RemoteFetch(name=name,
2200 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002201 initial=False, quiet=quiet, alt_dir=alt_dir,
2202 depth=None)
2203 else:
2204 # Avoid infinite recursion: sync all branches with depth set to None
2205 return self._RemoteFetch(name=name, current_branch_only=False,
2206 initial=False, quiet=quiet, alt_dir=alt_dir,
2207 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002208
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002209 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002210
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002211 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002212 if initial and \
2213 (self.manifest.manifestProject.config.GetString('repo.depth') or
2214 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002215 return False
2216
2217 remote = self.GetRemote(self.remote.name)
2218 bundle_url = remote.url + '/clone.bundle'
2219 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002220 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2221 'persistent-http',
2222 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002223 return False
2224
2225 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2226 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2227
2228 exist_dst = os.path.exists(bundle_dst)
2229 exist_tmp = os.path.exists(bundle_tmp)
2230
2231 if not initial and not exist_dst and not exist_tmp:
2232 return False
2233
2234 if not exist_dst:
2235 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2236 if not exist_dst:
2237 return False
2238
2239 cmd = ['fetch']
2240 if quiet:
2241 cmd.append('--quiet')
2242 if not self.worktree:
2243 cmd.append('--update-head-ok')
2244 cmd.append(bundle_dst)
2245 for f in remote.fetch:
2246 cmd.append(str(f))
2247 cmd.append('refs/tags/*:refs/tags/*')
2248
2249 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002251 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002252 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002253 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002254 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002255
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002256 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002257 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002258 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002259
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002260 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002261 if quiet:
2262 cmd += ['--silent']
2263 if os.path.exists(tmpPath):
2264 size = os.stat(tmpPath).st_size
2265 if size >= 1024:
2266 cmd += ['--continue-at', '%d' % (size,)]
2267 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002268 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002269 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2270 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002271 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002272 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002273 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002274 if srcUrl.startswith('persistent-'):
2275 srcUrl = srcUrl[len('persistent-'):]
2276 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002277
Dave Borowitz137d0132015-01-02 11:12:54 -08002278 if IsTrace():
2279 Trace('%s', ' '.join(cmd))
2280 try:
2281 proc = subprocess.Popen(cmd)
2282 except OSError:
2283 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002284
Dave Borowitz137d0132015-01-02 11:12:54 -08002285 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002286
Dave Borowitz137d0132015-01-02 11:12:54 -08002287 if curlret == 22:
2288 # From curl man page:
2289 # 22: HTTP page not retrieved. The requested url was not found or
2290 # returned another error with the HTTP error code being 400 or above.
2291 # This return code only appears if -f, --fail is used.
2292 if not quiet:
2293 print("Server does not provide clone.bundle; ignoring.",
2294 file=sys.stderr)
2295 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002296
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002297 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002298 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002299 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002300 return True
2301 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002302 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002303 return False
2304 else:
2305 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002306
Kris Giesingc8d882a2014-12-23 13:02:32 -08002307 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002308 try:
2309 with open(path) as f:
2310 if f.read(16) == '# v2 git bundle\n':
2311 return True
2312 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002313 if not quiet:
2314 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002315 return False
2316 except OSError:
2317 return False
2318
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002319 def _Checkout(self, rev, quiet=False):
2320 cmd = ['checkout']
2321 if quiet:
2322 cmd.append('-q')
2323 cmd.append(rev)
2324 cmd.append('--')
2325 if GitCommand(self, cmd).Wait() != 0:
2326 if self._allrefs:
2327 raise GitError('%s checkout %s ' % (self.name, rev))
2328
Anthony King7bdac712014-07-16 12:56:40 +01002329 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002330 cmd = ['cherry-pick']
2331 cmd.append(rev)
2332 cmd.append('--')
2333 if GitCommand(self, cmd).Wait() != 0:
2334 if self._allrefs:
2335 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2336
Anthony King7bdac712014-07-16 12:56:40 +01002337 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002338 cmd = ['revert']
2339 cmd.append('--no-edit')
2340 cmd.append(rev)
2341 cmd.append('--')
2342 if GitCommand(self, cmd).Wait() != 0:
2343 if self._allrefs:
2344 raise GitError('%s revert %s ' % (self.name, rev))
2345
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002346 def _ResetHard(self, rev, quiet=True):
2347 cmd = ['reset', '--hard']
2348 if quiet:
2349 cmd.append('-q')
2350 cmd.append(rev)
2351 if GitCommand(self, cmd).Wait() != 0:
2352 raise GitError('%s reset --hard %s ' % (self.name, rev))
2353
Martin Kellye4e94d22017-03-21 16:05:12 -07002354 def _SyncSubmodules(self, quiet=True):
2355 cmd = ['submodule', 'update', '--init', '--recursive']
2356 if quiet:
2357 cmd.append('-q')
2358 if GitCommand(self, cmd).Wait() != 0:
2359 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2360
Anthony King7bdac712014-07-16 12:56:40 +01002361 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002362 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002363 if onto is not None:
2364 cmd.extend(['--onto', onto])
2365 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002366 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002367 raise GitError('%s rebase %s ' % (self.name, upstream))
2368
Pierre Tardy3d125942012-05-04 12:18:12 +02002369 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002370 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002371 if ffonly:
2372 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002373 if GitCommand(self, cmd).Wait() != 0:
2374 raise GitError('%s merge %s ' % (self.name, head))
2375
Kevin Degiabaa7f32014-11-12 11:27:45 -07002376 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002377 init_git_dir = not os.path.exists(self.gitdir)
2378 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002379 try:
2380 # Initialize the bare repository, which contains all of the objects.
2381 if init_obj_dir:
2382 os.makedirs(self.objdir)
2383 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002384
Kevin Degib1a07b82015-07-27 13:33:43 -06002385 # If we have a separate directory to hold refs, initialize it as well.
2386 if self.objdir != self.gitdir:
2387 if init_git_dir:
2388 os.makedirs(self.gitdir)
2389
2390 if init_obj_dir or init_git_dir:
2391 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2392 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002393 try:
2394 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2395 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002396 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002397 print("Retrying clone after deleting %s" %
2398 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002399 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002400 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2401 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002402 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002403 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002404 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2405 except:
2406 raise e
2407 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002408
Kevin Degi384b3c52014-10-16 16:02:58 -06002409 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002410 mp = self.manifest.manifestProject
2411 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002412
Kevin Degib1a07b82015-07-27 13:33:43 -06002413 if ref_dir or mirror_git:
2414 if not mirror_git:
2415 mirror_git = os.path.join(ref_dir, self.name + '.git')
2416 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2417 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002418
Kevin Degib1a07b82015-07-27 13:33:43 -06002419 if os.path.exists(mirror_git):
2420 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002421
Kevin Degib1a07b82015-07-27 13:33:43 -06002422 elif os.path.exists(repo_git):
2423 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002424
Kevin Degib1a07b82015-07-27 13:33:43 -06002425 else:
2426 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002427
Kevin Degib1a07b82015-07-27 13:33:43 -06002428 if ref_dir:
2429 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2430 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002431
Kevin Degib1a07b82015-07-27 13:33:43 -06002432 self._UpdateHooks()
2433
2434 m = self.manifest.manifestProject.config
2435 for key in ['user.name', 'user.email']:
2436 if m.Has(key, include_defaults=False):
2437 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002438 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002439 if self.manifest.IsMirror:
2440 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002441 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002442 self.config.SetString('core.bare', None)
2443 except Exception:
2444 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002445 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002446 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002447 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002448 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002449
Jimmie Westera0444582012-10-24 13:44:42 +02002450 def _UpdateHooks(self):
2451 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002452 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002453
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002454 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002455 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002456 if not os.path.exists(hooks):
2457 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002458 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002459 name = os.path.basename(stock_hook)
2460
Victor Boivie65e0f352011-04-18 11:23:29 +02002461 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002462 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002463 # Don't install a Gerrit Code Review hook if this
2464 # project does not appear to use it for reviews.
2465 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002466 # Since the manifest project is one of those, but also
2467 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002468 continue
2469
2470 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002471 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002472 continue
2473 if os.path.exists(dst):
2474 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002475 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002476 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002477 _warn("%s: Not replacing locally modified %s hook",
2478 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002479 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002480 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002481 platform_utils.symlink(
2482 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002483 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002484 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002485 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002486 else:
2487 raise
2488
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002489 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002490 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002491 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002492 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002493 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002494 remote.review = self.remote.review
2495 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002496
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002497 if self.worktree:
2498 remote.ResetFetch(mirror=False)
2499 else:
2500 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002501 remote.Save()
2502
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002503 def _InitMRef(self):
2504 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002505 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002506
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002507 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002508 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002509
2510 def _InitAnyMRef(self, ref):
2511 cur = self.bare_ref.symref(ref)
2512
2513 if self.revisionId:
2514 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2515 msg = 'manifest set to %s' % self.revisionId
2516 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002517 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002518 else:
2519 remote = self.GetRemote(self.remote.name)
2520 dst = remote.ToLocal(self.revisionExpr)
2521 if cur != dst:
2522 msg = 'manifest set to %s' % self.revisionExpr
2523 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002524
Kevin Degi384b3c52014-10-16 16:02:58 -06002525 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002526 symlink_files = self.shareable_files[:]
2527 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002528 if share_refs:
2529 symlink_files += self.working_tree_files
2530 symlink_dirs += self.working_tree_dirs
2531 to_symlink = symlink_files + symlink_dirs
2532 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002533 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002534 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002535 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002536 # Fail if the links are pointing to the wrong place
2537 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002538 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002539 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002540 'work tree. If you\'re comfortable with the '
2541 'possibility of losing the work tree\'s git metadata,'
2542 ' use `repo sync --force-sync {0}` to '
2543 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002544
David James8d201162013-10-11 17:03:19 -07002545 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2546 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2547
2548 Args:
2549 gitdir: The bare git repository. Must already be initialized.
2550 dotgit: The repository you would like to initialize.
2551 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2552 Only one work tree can store refs under a given |gitdir|.
2553 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2554 This saves you the effort of initializing |dotgit| yourself.
2555 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002556 symlink_files = self.shareable_files[:]
2557 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002558 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002559 symlink_files += self.working_tree_files
2560 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002561 to_symlink = symlink_files + symlink_dirs
2562
2563 to_copy = []
2564 if copy_all:
2565 to_copy = os.listdir(gitdir)
2566
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002567 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002568 for name in set(to_copy).union(to_symlink):
2569 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002570 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002571 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002572
Kevin Degi384b3c52014-10-16 16:02:58 -06002573 if os.path.lexists(dst):
2574 continue
David James8d201162013-10-11 17:03:19 -07002575
2576 # If the source dir doesn't exist, create an empty dir.
2577 if name in symlink_dirs and not os.path.lexists(src):
2578 os.makedirs(src)
2579
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002580 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002581 platform_utils.symlink(
2582 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002583 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002584 if os.path.isdir(src):
2585 shutil.copytree(src, dst)
2586 elif os.path.isfile(src):
2587 shutil.copy(src, dst)
2588
Conley Owens80b87fe2014-05-09 17:13:44 -07002589 # If the source file doesn't exist, ensure the destination
2590 # file doesn't either.
2591 if name in symlink_files and not os.path.lexists(src):
2592 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002593 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002594 except OSError:
2595 pass
2596
David James8d201162013-10-11 17:03:19 -07002597 except OSError as e:
2598 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002599 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002600 else:
2601 raise
2602
Martin Kellye4e94d22017-03-21 16:05:12 -07002603 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002604 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002605 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002606 try:
2607 if init_dotgit:
2608 os.makedirs(dotgit)
2609 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2610 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002611
Kevin Degiabaa7f32014-11-12 11:27:45 -07002612 try:
2613 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2614 except GitError as e:
2615 if force_sync:
2616 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002617 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002618 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002619 except:
2620 raise e
2621 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002622
Kevin Degib1a07b82015-07-27 13:33:43 -06002623 if init_dotgit:
2624 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002625
Kevin Degib1a07b82015-07-27 13:33:43 -06002626 cmd = ['read-tree', '--reset', '-u']
2627 cmd.append('-v')
2628 cmd.append(HEAD)
2629 if GitCommand(self, cmd).Wait() != 0:
2630 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002631
Martin Kellye4e94d22017-03-21 16:05:12 -07002632 if submodules:
2633 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002634 self._CopyAndLinkFiles()
2635 except Exception:
2636 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002637 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002638 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002639
Renaud Paquay788e9622017-01-27 11:41:12 -08002640 def _get_symlink_error_message(self):
2641 if platform_utils.isWindows():
2642 return ('Unable to create symbolic link. Please re-run the command as '
2643 'Administrator, or see '
2644 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2645 'for other options.')
2646 return 'filesystem must support symlinks'
2647
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002648 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002649 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002650
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002651 def _revlist(self, *args, **kw):
2652 a = []
2653 a.extend(args)
2654 a.append('--')
2655 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002656
2657 @property
2658 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002659 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002660
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002661 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002662 """Get logs between two revisions of this project."""
2663 comp = '..'
2664 if rev1:
2665 revs = [rev1]
2666 if rev2:
2667 revs.extend([comp, rev2])
2668 cmd = ['log', ''.join(revs)]
2669 out = DiffColoring(self.config)
2670 if out.is_on and color:
2671 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002672 if pretty_format is not None:
2673 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002674 if oneline:
2675 cmd.append('--oneline')
2676
2677 try:
2678 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2679 if log.Wait() == 0:
2680 return log.stdout
2681 except GitError:
2682 # worktree may not exist if groups changed for example. In that case,
2683 # try in gitdir instead.
2684 if not os.path.exists(self.worktree):
2685 return self.bare_git.log(*cmd[1:])
2686 else:
2687 raise
2688 return None
2689
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002690 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2691 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002692 """Get the list of logs from this revision to given revisionId"""
2693 logs = {}
2694 selfId = self.GetRevisionId(self._allrefs)
2695 toId = toProject.GetRevisionId(toProject._allrefs)
2696
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002697 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2698 pretty_format=pretty_format)
2699 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2700 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002701 return logs
2702
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002703 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002704
David James8d201162013-10-11 17:03:19 -07002705 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002706 self._project = project
2707 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002708 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002710 def LsOthers(self):
2711 p = GitCommand(self._project,
2712 ['ls-files',
2713 '-z',
2714 '--others',
2715 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002716 bare=False,
David James8d201162013-10-11 17:03:19 -07002717 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002718 capture_stdout=True,
2719 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002720 if p.Wait() == 0:
2721 out = p.stdout
2722 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002723 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002724 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002725 return []
2726
2727 def DiffZ(self, name, *args):
2728 cmd = [name]
2729 cmd.append('-z')
2730 cmd.extend(args)
2731 p = GitCommand(self._project,
2732 cmd,
David James8d201162013-10-11 17:03:19 -07002733 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002734 bare=False,
2735 capture_stdout=True,
2736 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002737 try:
2738 out = p.process.stdout.read()
2739 r = {}
2740 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002741 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002742 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002743 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002744 info = next(out)
2745 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002746 except StopIteration:
2747 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002748
2749 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002750
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002751 def __init__(self, path, omode, nmode, oid, nid, state):
2752 self.path = path
2753 self.src_path = None
2754 self.old_mode = omode
2755 self.new_mode = nmode
2756 self.old_id = oid
2757 self.new_id = nid
2758
2759 if len(state) == 1:
2760 self.status = state
2761 self.level = None
2762 else:
2763 self.status = state[:1]
2764 self.level = state[1:]
2765 while self.level.startswith('0'):
2766 self.level = self.level[1:]
2767
2768 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002769 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002770 if info.status in ('R', 'C'):
2771 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002772 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002773 r[info.path] = info
2774 return r
2775 finally:
2776 p.Wait()
2777
2778 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002779 if self._bare:
2780 path = os.path.join(self._project.gitdir, HEAD)
2781 else:
2782 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002783 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002784 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002785 except IOError as e:
2786 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002787 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002788 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002789 finally:
2790 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302791 try:
2792 line = line.decode()
2793 except AttributeError:
2794 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002795 if line.startswith('ref: '):
2796 return line[5:-1]
2797 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002798
2799 def SetHead(self, ref, message=None):
2800 cmdv = []
2801 if message is not None:
2802 cmdv.extend(['-m', message])
2803 cmdv.append(HEAD)
2804 cmdv.append(ref)
2805 self.symbolic_ref(*cmdv)
2806
2807 def DetachHead(self, new, message=None):
2808 cmdv = ['--no-deref']
2809 if message is not None:
2810 cmdv.extend(['-m', message])
2811 cmdv.append(HEAD)
2812 cmdv.append(new)
2813 self.update_ref(*cmdv)
2814
2815 def UpdateRef(self, name, new, old=None,
2816 message=None,
2817 detach=False):
2818 cmdv = []
2819 if message is not None:
2820 cmdv.extend(['-m', message])
2821 if detach:
2822 cmdv.append('--no-deref')
2823 cmdv.append(name)
2824 cmdv.append(new)
2825 if old is not None:
2826 cmdv.append(old)
2827 self.update_ref(*cmdv)
2828
2829 def DeleteRef(self, name, old=None):
2830 if not old:
2831 old = self.rev_parse(name)
2832 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002833 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002835 def rev_list(self, *args, **kw):
2836 if 'format' in kw:
2837 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2838 else:
2839 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840 cmdv.extend(args)
2841 p = GitCommand(self._project,
2842 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002843 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002844 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002845 capture_stdout=True,
2846 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002847 r = []
2848 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002849 if line[-1] == '\n':
2850 line = line[:-1]
2851 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002852 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002853 raise GitError('%s rev-list %s: %s' %
2854 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002855 return r
2856
2857 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002858 """Allow arbitrary git commands using pythonic syntax.
2859
2860 This allows you to do things like:
2861 git_obj.rev_parse('HEAD')
2862
2863 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2864 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002865 Any other positional arguments will be passed to the git command, and the
2866 following keyword arguments are supported:
2867 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002868
2869 Args:
2870 name: The name of the git command to call. Any '_' characters will
2871 be replaced with '-'.
2872
2873 Returns:
2874 A callable object that will try to call git with the named command.
2875 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002876 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002877
Dave Borowitz091f8932012-10-23 17:01:04 -07002878 def runner(*args, **kwargs):
2879 cmdv = []
2880 config = kwargs.pop('config', None)
2881 for k in kwargs:
2882 raise TypeError('%s() got an unexpected keyword argument %r'
2883 % (name, k))
2884 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002885 if not git_require((1, 7, 2)):
2886 raise ValueError('cannot set config on command line for %s()'
2887 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302888 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002889 cmdv.append('-c')
2890 cmdv.append('%s=%s' % (k, v))
2891 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892 cmdv.extend(args)
2893 p = GitCommand(self._project,
2894 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002895 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002896 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002897 capture_stdout=True,
2898 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002899 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002900 raise GitError('%s %s: %s' %
2901 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002902 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302903 try:
Conley Owensedd01512013-09-26 12:59:58 -07002904 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302905 except AttributeError:
2906 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002907 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2908 return r[:-1]
2909 return r
2910 return runner
2911
2912
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002913class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002914
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002915 def __str__(self):
2916 return 'prior sync failed; rebase still in progress'
2917
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002918
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002919class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002920
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002921 def __str__(self):
2922 return 'contains uncommitted changes'
2923
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002924
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002925class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002926
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002927 def __init__(self, project, text):
2928 self.project = project
2929 self.text = text
2930
2931 def Print(self, syncbuf):
2932 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2933 syncbuf.out.nl()
2934
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002935
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002936class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002937
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002938 def __init__(self, project, why):
2939 self.project = project
2940 self.why = why
2941
2942 def Print(self, syncbuf):
2943 syncbuf.out.fail('error: %s/: %s',
2944 self.project.relpath,
2945 str(self.why))
2946 syncbuf.out.nl()
2947
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002948
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002949class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002950
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002951 def __init__(self, project, action):
2952 self.project = project
2953 self.action = action
2954
2955 def Run(self, syncbuf):
2956 out = syncbuf.out
2957 out.project('project %s/', self.project.relpath)
2958 out.nl()
2959 try:
2960 self.action()
2961 out.nl()
2962 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002963 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002964 out.nl()
2965 return False
2966
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002967
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002968class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002969
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002970 def __init__(self, config):
2971 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002972 self.project = self.printer('header', attr='bold')
2973 self.info = self.printer('info')
2974 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002975
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002976
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002977class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002978
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002979 def __init__(self, config, detach_head=False):
2980 self._messages = []
2981 self._failures = []
2982 self._later_queue1 = []
2983 self._later_queue2 = []
2984
2985 self.out = _SyncColoring(config)
2986 self.out.redirect(sys.stderr)
2987
2988 self.detach_head = detach_head
2989 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002990 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002991
2992 def info(self, project, fmt, *args):
2993 self._messages.append(_InfoMessage(project, fmt % args))
2994
2995 def fail(self, project, err=None):
2996 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002997 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002998
2999 def later1(self, project, what):
3000 self._later_queue1.append(_Later(project, what))
3001
3002 def later2(self, project, what):
3003 self._later_queue2.append(_Later(project, what))
3004
3005 def Finish(self):
3006 self._PrintMessages()
3007 self._RunLater()
3008 self._PrintMessages()
3009 return self.clean
3010
David Rileye0684ad2017-04-05 00:02:59 -07003011 def Recently(self):
3012 recent_clean = self.recent_clean
3013 self.recent_clean = True
3014 return recent_clean
3015
3016 def _MarkUnclean(self):
3017 self.clean = False
3018 self.recent_clean = False
3019
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003020 def _RunLater(self):
3021 for q in ['_later_queue1', '_later_queue2']:
3022 if not self._RunQueue(q):
3023 return
3024
3025 def _RunQueue(self, queue):
3026 for m in getattr(self, queue):
3027 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003028 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003029 return False
3030 setattr(self, queue, [])
3031 return True
3032
3033 def _PrintMessages(self):
3034 for m in self._messages:
3035 m.Print(self)
3036 for m in self._failures:
3037 m.Print(self)
3038
3039 self._messages = []
3040 self._failures = []
3041
3042
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003043class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003044
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003045 """A special project housed under .repo.
3046 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003047
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003048 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003049 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003050 manifest=manifest,
3051 name=name,
3052 gitdir=gitdir,
3053 objdir=gitdir,
3054 worktree=worktree,
3055 remote=RemoteSpec('origin'),
3056 relpath='.repo/%s' % name,
3057 revisionExpr='refs/heads/master',
3058 revisionId=None,
3059 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003060
3061 def PreSync(self):
3062 if self.Exists:
3063 cb = self.CurrentBranch
3064 if cb:
3065 base = self.GetBranch(cb).merge
3066 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003067 self.revisionExpr = base
3068 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003069
Martin Kelly224a31a2017-07-10 14:46:25 -07003070 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003071 """ Prepare MetaProject for manifest branch switch
3072 """
3073
3074 # detach and delete manifest branch, allowing a new
3075 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003076 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003077 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003078 syncbuf.Finish()
3079
3080 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003081 ['update-ref', '-d', 'refs/heads/default'],
3082 capture_stdout=True,
3083 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003084
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003085 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003086 def LastFetch(self):
3087 try:
3088 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3089 return os.path.getmtime(fh)
3090 except OSError:
3091 return 0
3092
3093 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003094 def HasChanges(self):
3095 """Has the remote received new commits not yet checked out?
3096 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003097 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003098 return False
3099
David Pursehouse8a68ff92012-09-24 12:15:13 +09003100 all_refs = self.bare_ref.all
3101 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003102 head = self.work_git.GetHead()
3103 if head.startswith(R_HEADS):
3104 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003105 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003106 except KeyError:
3107 head = None
3108
3109 if revid == head:
3110 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003111 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003112 return True
3113 return False