blob: 589425149c7148111e7fec7615618ab35c8a8da5 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
2#
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080018import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070020import glob
Mike Frysingerf7c51602019-06-18 17:23:39 -040021import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070023import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import re
25import shutil
26import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070027import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020029import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080030import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070031import time
Dave Borowitz137d0132015-01-02 11:12:54 -080032import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070033
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070035from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070036from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
37 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070038from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080039from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080040from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070041import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070042from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
Shawn O. Pearced237b692009-04-17 18:49:50 -070044from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070045
David Pursehouse59bbb582013-05-17 10:49:33 +090046from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040047if is_python3():
48 import urllib.parse
49else:
50 import imp
51 import urlparse
52 urllib = imp.new_module('urllib')
53 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054 input = raw_input
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')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700109 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700110 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,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000180 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700182 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200183 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200184 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800185 validate_certs=True,
186 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800187 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700188 people,
Brian Harring435370c2012-07-28 15:37:04 -0700189 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000190 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700192 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200193 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200194 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800195 validate_certs=validate_certs,
196 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700197
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700198 def GetPublishedRefs(self):
199 refs = {}
200 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700201 self.branch.remote.SshReviewUrl(self.project.UserEmail),
202 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700203 for line in output.split('\n'):
204 try:
205 (sha, ref) = line.split()
206 refs[sha] = ref
207 except ValueError:
208 pass
209
210 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700214
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215 def __init__(self, config):
216 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100217 self.project = self.printer('header', attr='bold')
218 self.branch = self.printer('header', attr='bold')
219 self.nobranch = self.printer('nobranch', fg='red')
220 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
Anthony King7bdac712014-07-16 12:56:40 +0100222 self.added = self.printer('added', fg='green')
223 self.changed = self.printer('changed', fg='red')
224 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700225
226
227class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229 def __init__(self, config):
230 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100231 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
Anthony King7bdac712014-07-16 12:56:40 +0100234class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
James W. Mills24c13082012-04-12 15:04:13 -0500236 def __init__(self, name, value, keep):
237 self.name = name
238 self.value = value
239 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Anthony King7bdac712014-07-16 12:56:40 +0100242class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700243
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700245 self.src = src
246 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800247 self.abs_src = abssrc
248 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
250 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800251 src = self.abs_src
252 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253 # copy file if it does not exist or is out of date
254 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
255 try:
256 # remove existing file first, since it might be read-only
257 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800258 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400259 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700261 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200262 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263 shutil.copy(src, dest)
264 # make the file read-only
265 mode = os.stat(dest)[stat.ST_MODE]
266 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
267 os.chmod(dest, mode)
268 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700269 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Anthony King7bdac712014-07-16 12:56:40 +0100272class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700273
Wink Saville4c426ef2015-06-03 08:05:17 -0700274 def __init__(self, git_worktree, src, dest, relsrc, absdest):
275 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.src = src
277 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700278 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 self.abs_dest = absdest
280
Wink Saville4c426ef2015-06-03 08:05:17 -0700281 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700283 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500284 try:
285 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800286 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800287 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700289 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700290 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500291 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700292 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500293 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700294 _error('Cannot link file %s to %s', relSrc, absDest)
295
296 def _Link(self):
297 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
298 on the src linking all of the files in the source in to the destination
299 directory.
300 """
301 # We use the absSrc to handle the situation where the current directory
302 # is not the root of the repo
303 absSrc = os.path.join(self.git_worktree, self.src)
304 if os.path.exists(absSrc):
305 # Entity exists so just a simple one to one link operation
306 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
307 else:
308 # Entity doesn't exist assume there is a wild card
309 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700310 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700312 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700313 else:
314 absSrcFiles = glob.glob(absSrc)
315 for absSrcFile in absSrcFiles:
316 # Create a releative path from source dir to destination dir
317 absSrcDir = os.path.dirname(absSrcFile)
318 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
319
320 # Get the source file name
321 srcFile = os.path.basename(absSrcFile)
322
323 # Now form the final full paths to srcFile. They will be
324 # absolute for the desintaiton and relative for the srouce.
325 absDest = os.path.join(absDestDir, srcFile)
326 relSrc = os.path.join(relSrcDir, srcFile)
327 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500328
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700331
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700332 def __init__(self,
333 name,
Anthony King7bdac712014-07-16 12:56:40 +0100334 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700335 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100336 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700337 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700338 orig_name=None,
339 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.name = name
341 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700342 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700343 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100344 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700345 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700346 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700350
Doug Anderson37282b42011-03-04 11:54:18 -0800351 """A RepoHook contains information about a script to run as a hook.
352
353 Hooks are used to run a python script before running an upload (for instance,
354 to run presubmit checks). Eventually, we may have hooks for other actions.
355
356 This shouldn't be confused with files in the 'repo/hooks' directory. Those
357 files are copied into each '.git/hooks' folder for each project. Repo-level
358 hooks are associated instead with repo actions.
359
360 Hooks are always python. When a hook is run, we will load the hook into the
361 interpreter and execute its main() function.
362 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700363
Doug Anderson37282b42011-03-04 11:54:18 -0800364 def __init__(self,
365 hook_type,
366 hooks_project,
367 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400368 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800369 abort_if_user_denies=False):
370 """RepoHook constructor.
371
372 Params:
373 hook_type: A string representing the type of hook. This is also used
374 to figure out the name of the file containing the hook. For
375 example: 'pre-upload'.
376 hooks_project: The project containing the repo hooks. If you have a
377 manifest, this is manifest.repo_hooks_project. OK if this is None,
378 which will make the hook a no-op.
379 topdir: Repo's top directory (the one containing the .repo directory).
380 Scripts will run with CWD as this directory. If you have a manifest,
381 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400382 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800383 abort_if_user_denies: If True, we'll throw a HookError() if the user
384 doesn't allow us to run the hook.
385 """
386 self._hook_type = hook_type
387 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400388 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800389 self._topdir = topdir
390 self._abort_if_user_denies = abort_if_user_denies
391
392 # Store the full path to the script for convenience.
393 if self._hooks_project:
394 self._script_fullpath = os.path.join(self._hooks_project.worktree,
395 self._hook_type + '.py')
396 else:
397 self._script_fullpath = None
398
399 def _GetHash(self):
400 """Return a hash of the contents of the hooks directory.
401
402 We'll just use git to do this. This hash has the property that if anything
403 changes in the directory we will return a different has.
404
405 SECURITY CONSIDERATION:
406 This hash only represents the contents of files in the hook directory, not
407 any other files imported or called by hooks. Changes to imported files
408 can change the script behavior without affecting the hash.
409
410 Returns:
411 A string representing the hash. This will always be ASCII so that it can
412 be printed to the user easily.
413 """
414 assert self._hooks_project, "Must have hooks to calculate their hash."
415
416 # We will use the work_git object rather than just calling GetRevisionId().
417 # That gives us a hash of the latest checked in version of the files that
418 # the user will actually be executing. Specifically, GetRevisionId()
419 # doesn't appear to change even if a user checks out a different version
420 # of the hooks repo (via git checkout) nor if a user commits their own revs.
421 #
422 # NOTE: Local (non-committed) changes will not be factored into this hash.
423 # I think this is OK, since we're really only worried about warning the user
424 # about upstream changes.
425 return self._hooks_project.work_git.rev_parse('HEAD')
426
427 def _GetMustVerb(self):
428 """Return 'must' if the hook is required; 'should' if not."""
429 if self._abort_if_user_denies:
430 return 'must'
431 else:
432 return 'should'
433
434 def _CheckForHookApproval(self):
435 """Check to see whether this hook has been approved.
436
Mike Frysinger40252c22016-08-15 21:23:44 -0400437 We'll accept approval of manifest URLs if they're using secure transports.
438 This way the user can say they trust the manifest hoster. For insecure
439 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800440
441 Note that we ask permission for each individual hook even though we use
442 the hash of all hooks when detecting changes. We'd like the user to be
443 able to approve / deny each hook individually. We only use the hash of all
444 hooks because there is no other easy way to detect changes to local imports.
445
446 Returns:
447 True if this hook is approved to run; False otherwise.
448
449 Raises:
450 HookError: Raised if the user doesn't approve and abort_if_user_denies
451 was passed to the consturctor.
452 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400453 if self._ManifestUrlHasSecureScheme():
454 return self._CheckForHookApprovalManifest()
455 else:
456 return self._CheckForHookApprovalHash()
457
458 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
459 changed_prompt):
460 """Check for approval for a particular attribute and hook.
461
462 Args:
463 subkey: The git config key under [repo.hooks.<hook_type>] to store the
464 last approved string.
465 new_val: The new value to compare against the last approved one.
466 main_prompt: Message to display to the user to ask for approval.
467 changed_prompt: Message explaining why we're re-asking for approval.
468
469 Returns:
470 True if this hook is approved to run; False otherwise.
471
472 Raises:
473 HookError: Raised if the user doesn't approve and abort_if_user_denies
474 was passed to the consturctor.
475 """
Doug Anderson37282b42011-03-04 11:54:18 -0800476 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 # Get the last value that the user approved for this hook; may be None.
480 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800481
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400484 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800485 # Approval matched. We're done.
486 return True
487 else:
488 # Give the user a reason why we're prompting, since they last told
489 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400490 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800491 else:
492 prompt = ''
493
494 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
495 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400496 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530497 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900498 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800499
500 # User is doing a one-time approval.
501 if response in ('y', 'yes'):
502 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400503 elif response == 'always':
504 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800505 return True
506
507 # For anything else, we'll assume no approval.
508 if self._abort_if_user_denies:
509 raise HookError('You must allow the %s hook or use --no-verify.' %
510 self._hook_type)
511
512 return False
513
Mike Frysinger40252c22016-08-15 21:23:44 -0400514 def _ManifestUrlHasSecureScheme(self):
515 """Check if the URI for the manifest is a secure transport."""
516 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
517 parse_results = urllib.parse.urlparse(self._manifest_url)
518 return parse_results.scheme in secure_schemes
519
520 def _CheckForHookApprovalManifest(self):
521 """Check whether the user has approved this manifest host.
522
523 Returns:
524 True if this hook is approved to run; False otherwise.
525 """
526 return self._CheckForHookApprovalHelper(
527 'approvedmanifest',
528 self._manifest_url,
529 'Run hook scripts from %s' % (self._manifest_url,),
530 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
531
532 def _CheckForHookApprovalHash(self):
533 """Check whether the user has approved the hooks repo.
534
535 Returns:
536 True if this hook is approved to run; False otherwise.
537 """
538 prompt = ('Repo %s run the script:\n'
539 ' %s\n'
540 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700541 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400542 return self._CheckForHookApprovalHelper(
543 'approvedhash',
544 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700545 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400546 'Scripts have changed since %s was allowed.' % (self._hook_type,))
547
Mike Frysingerf7c51602019-06-18 17:23:39 -0400548 @staticmethod
549 def _ExtractInterpFromShebang(data):
550 """Extract the interpreter used in the shebang.
551
552 Try to locate the interpreter the script is using (ignoring `env`).
553
554 Args:
555 data: The file content of the script.
556
557 Returns:
558 The basename of the main script interpreter, or None if a shebang is not
559 used or could not be parsed out.
560 """
561 firstline = data.splitlines()[:1]
562 if not firstline:
563 return None
564
565 # The format here can be tricky.
566 shebang = firstline[0].strip()
567 m = re.match(r'^#!\s*([^\s]+)(?:\s+([^\s]+))?', shebang)
568 if not m:
569 return None
570
571 # If the using `env`, find the target program.
572 interp = m.group(1)
573 if os.path.basename(interp) == 'env':
574 interp = m.group(2)
575
576 return interp
577
578 def _ExecuteHookViaReexec(self, interp, context, **kwargs):
579 """Execute the hook script through |interp|.
580
581 Note: Support for this feature should be dropped ~Jun 2021.
582
583 Args:
584 interp: The Python program to run.
585 context: Basic Python context to execute the hook inside.
586 kwargs: Arbitrary arguments to pass to the hook script.
587
588 Raises:
589 HookError: When the hooks failed for any reason.
590 """
591 # This logic needs to be kept in sync with _ExecuteHookViaImport below.
592 script = """
593import json, os, sys
594path = '''%(path)s'''
595kwargs = json.loads('''%(kwargs)s''')
596context = json.loads('''%(context)s''')
597sys.path.insert(0, os.path.dirname(path))
598data = open(path).read()
599exec(compile(data, path, 'exec'), context)
600context['main'](**kwargs)
601""" % {
602 'path': self._script_fullpath,
603 'kwargs': json.dumps(kwargs),
604 'context': json.dumps(context),
605 }
606
607 # We pass the script via stdin to avoid OS argv limits. It also makes
608 # unhandled exception tracebacks less verbose/confusing for users.
609 cmd = [interp, '-c', 'import sys; exec(sys.stdin.read())']
610 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
611 proc.communicate(input=script.encode('utf-8'))
612 if proc.returncode:
613 raise HookError('Failed to run %s hook.' % (self._hook_type,))
614
615 def _ExecuteHookViaImport(self, data, context, **kwargs):
616 """Execute the hook code in |data| directly.
617
618 Args:
619 data: The code of the hook to execute.
620 context: Basic Python context to execute the hook inside.
621 kwargs: Arbitrary arguments to pass to the hook script.
622
623 Raises:
624 HookError: When the hooks failed for any reason.
625 """
626 # Exec, storing global context in the context dict. We catch exceptions
627 # and convert to a HookError w/ just the failing traceback.
628 try:
629 exec(compile(data, self._script_fullpath, 'exec'), context)
630 except Exception:
631 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
632 (traceback.format_exc(), self._hook_type))
633
634 # Running the script should have defined a main() function.
635 if 'main' not in context:
636 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
637
638 # Call the main function in the hook. If the hook should cause the
639 # build to fail, it will raise an Exception. We'll catch that convert
640 # to a HookError w/ just the failing traceback.
641 try:
642 context['main'](**kwargs)
643 except Exception:
644 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
645 'above.' % (traceback.format_exc(), self._hook_type))
646
Doug Anderson37282b42011-03-04 11:54:18 -0800647 def _ExecuteHook(self, **kwargs):
648 """Actually execute the given hook.
649
650 This will run the hook's 'main' function in our python interpreter.
651
652 Args:
653 kwargs: Keyword arguments to pass to the hook. These are often specific
654 to the hook type. For instance, pre-upload hooks will contain
655 a project_list.
656 """
657 # Keep sys.path and CWD stashed away so that we can always restore them
658 # upon function exit.
659 orig_path = os.getcwd()
660 orig_syspath = sys.path
661
662 try:
663 # Always run hooks with CWD as topdir.
664 os.chdir(self._topdir)
665
666 # Put the hook dir as the first item of sys.path so hooks can do
667 # relative imports. We want to replace the repo dir as [0] so
668 # hooks can't import repo files.
669 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
670
Mike Frysingerf7c51602019-06-18 17:23:39 -0400671 # Initial global context for the hook to run within.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500672 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800673
Doug Anderson37282b42011-03-04 11:54:18 -0800674 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
675 # We don't actually want hooks to define their main with this argument--
676 # it's there to remind them that their hook should always take **kwargs.
677 # For instance, a pre-upload hook should be defined like:
678 # def main(project_list, **kwargs):
679 #
680 # This allows us to later expand the API without breaking old hooks.
681 kwargs = kwargs.copy()
682 kwargs['hook_should_take_kwargs'] = True
683
Mike Frysingerf7c51602019-06-18 17:23:39 -0400684 # See what version of python the hook has been written against.
685 data = open(self._script_fullpath).read()
686 interp = self._ExtractInterpFromShebang(data)
687 reexec = False
688 if interp:
689 prog = os.path.basename(interp)
690 if prog.startswith('python2') and sys.version_info.major != 2:
691 reexec = True
692 elif prog.startswith('python3') and sys.version_info.major == 2:
693 reexec = True
694
695 # Attempt to execute the hooks through the requested version of Python.
696 if reexec:
697 try:
698 self._ExecuteHookViaReexec(interp, context, **kwargs)
699 except OSError as e:
700 if e.errno == errno.ENOENT:
701 # We couldn't find the interpreter, so fallback to importing.
702 reexec = False
703 else:
704 raise
705
706 # Run the hook by importing directly.
707 if not reexec:
708 self._ExecuteHookViaImport(data, context, **kwargs)
Doug Anderson37282b42011-03-04 11:54:18 -0800709 finally:
710 # Restore sys.path and CWD.
711 sys.path = orig_syspath
712 os.chdir(orig_path)
713
714 def Run(self, user_allows_all_hooks, **kwargs):
715 """Run the hook.
716
717 If the hook doesn't exist (because there is no hooks project or because
718 this particular hook is not enabled), this is a no-op.
719
720 Args:
721 user_allows_all_hooks: If True, we will never prompt about running the
722 hook--we'll just assume it's OK to run it.
723 kwargs: Keyword arguments to pass to the hook. These are often specific
724 to the hook type. For instance, pre-upload hooks will contain
725 a project_list.
726
727 Raises:
728 HookError: If there was a problem finding the hook or the user declined
729 to run a required hook (from _CheckForHookApproval).
730 """
731 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700732 if ((not self._hooks_project) or (self._hook_type not in
733 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800734 return
735
736 # Bail with a nice error if we can't find the hook.
737 if not os.path.isfile(self._script_fullpath):
738 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
739
740 # Make sure the user is OK with running the hook.
741 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
742 return
743
744 # Run the hook with the same version of python we're using.
745 self._ExecuteHook(**kwargs)
746
747
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700748class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600749 # These objects can be shared between several working trees.
750 shareable_files = ['description', 'info']
751 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
752 # These objects can only be used by a single working tree.
753 working_tree_files = ['config', 'packed-refs', 'shallow']
754 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700755
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756 def __init__(self,
757 manifest,
758 name,
759 remote,
760 gitdir,
David James8d201162013-10-11 17:03:19 -0700761 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762 worktree,
763 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700764 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800765 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100766 rebase=True,
767 groups=None,
768 sync_c=False,
769 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900770 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100771 clone_depth=None,
772 upstream=None,
773 parent=None,
774 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900775 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700776 optimized_fetch=False,
777 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800778 """Init a Project object.
779
780 Args:
781 manifest: The XmlManifest object.
782 name: The `name` attribute of manifest.xml's project element.
783 remote: RemoteSpec object specifying its remote's properties.
784 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700785 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800786 worktree: Absolute path of git working tree.
787 relpath: Relative path of git working tree to repo's top directory.
788 revisionExpr: The `revision` attribute of manifest.xml's project element.
789 revisionId: git commit id for checking out.
790 rebase: The `rebase` attribute of manifest.xml's project element.
791 groups: The `groups` attribute of manifest.xml's project element.
792 sync_c: The `sync-c` attribute of manifest.xml's project element.
793 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900794 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800795 upstream: The `upstream` attribute of manifest.xml's project element.
796 parent: The parent Project object.
797 is_derived: False if the project was explicitly defined in the manifest;
798 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400799 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900800 optimized_fetch: If True, when a project is set to a sha1 revision, only
801 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700802 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800803 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700804 self.manifest = manifest
805 self.name = name
806 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800807 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700808 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800809 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700810 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800811 else:
812 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700813 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700814 self.revisionExpr = revisionExpr
815
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700816 if revisionId is None \
817 and revisionExpr \
818 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700819 self.revisionId = revisionExpr
820 else:
821 self.revisionId = revisionId
822
Mike Pontillod3153822012-02-28 11:53:24 -0800823 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700824 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700825 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800826 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900827 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900828 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700829 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800830 self.parent = parent
831 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900832 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800833 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800834
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700835 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500837 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500838 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700839 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
840 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700841
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800842 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700843 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800844 else:
845 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700846 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700847 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700848 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400849 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700850 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700851
Doug Anderson37282b42011-03-04 11:54:18 -0800852 # This will be filled in if a project is later identified to be the
853 # project containing repo hooks.
854 self.enabled_repo_hooks = []
855
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700856 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800857 def Derived(self):
858 return self.is_derived
859
860 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700861 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700862 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863
864 @property
865 def CurrentBranch(self):
866 """Obtain the name of the currently checked out branch.
867 The branch name omits the 'refs/heads/' prefix.
868 None is returned if the project is on a detached HEAD.
869 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700870 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700871 if b.startswith(R_HEADS):
872 return b[len(R_HEADS):]
873 return None
874
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700875 def IsRebaseInProgress(self):
876 w = self.worktree
877 g = os.path.join(w, '.git')
878 return os.path.exists(os.path.join(g, 'rebase-apply')) \
879 or os.path.exists(os.path.join(g, 'rebase-merge')) \
880 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200881
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882 def IsDirty(self, consider_untracked=True):
883 """Is the working directory modified in some way?
884 """
885 self.work_git.update_index('-q',
886 '--unmerged',
887 '--ignore-missing',
888 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900889 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700890 return True
891 if self.work_git.DiffZ('diff-files'):
892 return True
893 if consider_untracked and self.work_git.LsOthers():
894 return True
895 return False
896
897 _userident_name = None
898 _userident_email = None
899
900 @property
901 def UserName(self):
902 """Obtain the user's personal name.
903 """
904 if self._userident_name is None:
905 self._LoadUserIdentity()
906 return self._userident_name
907
908 @property
909 def UserEmail(self):
910 """Obtain the user's email address. This is very likely
911 to be their Gerrit login.
912 """
913 if self._userident_email is None:
914 self._LoadUserIdentity()
915 return self._userident_email
916
917 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900918 u = self.bare_git.var('GIT_COMMITTER_IDENT')
919 m = re.compile("^(.*) <([^>]*)> ").match(u)
920 if m:
921 self._userident_name = m.group(1)
922 self._userident_email = m.group(2)
923 else:
924 self._userident_name = ''
925 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926
927 def GetRemote(self, name):
928 """Get the configuration for a single remote.
929 """
930 return self.config.GetRemote(name)
931
932 def GetBranch(self, name):
933 """Get the configuration for a single branch.
934 """
935 return self.config.GetBranch(name)
936
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700937 def GetBranches(self):
938 """Get all existing local branches.
939 """
940 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900941 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700942 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700943
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530944 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700945 if name.startswith(R_HEADS):
946 name = name[len(R_HEADS):]
947 b = self.GetBranch(name)
948 b.current = name == current
949 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900950 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700951 heads[name] = b
952
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530953 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700954 if name.startswith(R_PUB):
955 name = name[len(R_PUB):]
956 b = heads.get(name)
957 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900958 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700959
960 return heads
961
Colin Cross5acde752012-03-28 20:15:45 -0700962 def MatchesGroups(self, manifest_groups):
963 """Returns true if the manifest groups specified at init should cause
964 this project to be synced.
965 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700966 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700967
968 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700969 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700970 manifest_groups: "-group1,group2"
971 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500972
973 The special manifest group "default" will match any project that
974 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700975 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500976 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700977 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700978 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500979 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700980
Conley Owens971de8e2012-04-16 10:36:08 -0700981 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700982 for group in expanded_manifest_groups:
983 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700984 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700985 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700986 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700987
Conley Owens971de8e2012-04-16 10:36:08 -0700988 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700989
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700990# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700991 def UncommitedFiles(self, get_all=True):
992 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700994 Args:
995 get_all: a boolean, if True - get information about all different
996 uncommitted files. If False - return as soon as any kind of
997 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500998 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700999 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001000 self.work_git.update_index('-q',
1001 '--unmerged',
1002 '--ignore-missing',
1003 '--refresh')
1004 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001005 details.append("rebase in progress")
1006 if not get_all:
1007 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001008
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001009 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
1010 if changes:
1011 details.extend(changes)
1012 if not get_all:
1013 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001014
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001015 changes = self.work_git.DiffZ('diff-files').keys()
1016 if changes:
1017 details.extend(changes)
1018 if not get_all:
1019 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001020
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001021 changes = self.work_git.LsOthers()
1022 if changes:
1023 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001024
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001025 return details
1026
1027 def HasChanges(self):
1028 """Returns true if there are uncommitted changes.
1029 """
1030 if self.UncommitedFiles(get_all=False):
1031 return True
1032 else:
1033 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001034
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001035 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +02001037
1038 Args:
1039 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001040 quiet: If True then only print the project name. Do not print
1041 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 """
Renaud Paquaybed8b622018-09-27 10:46:58 -07001043 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001044 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +02001045 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -07001046 print(file=output_redir)
1047 print('project %s/' % self.relpath, file=output_redir)
1048 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049 return
1050
1051 self.work_git.update_index('-q',
1052 '--unmerged',
1053 '--ignore-missing',
1054 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001055 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
1057 df = self.work_git.DiffZ('diff-files')
1058 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +01001059 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001060 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001061
1062 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001063 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +02001064 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -07001065 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001067 if quiet:
1068 out.nl()
1069 return 'DIRTY'
1070
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 branch = self.CurrentBranch
1072 if branch is None:
1073 out.nobranch('(*** NO BRANCH ***)')
1074 else:
1075 out.branch('branch %s', branch)
1076 out.nl()
1077
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001078 if rb:
1079 out.important('prior sync failed; rebase still in progress')
1080 out.nl()
1081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 paths = list()
1083 paths.extend(di.keys())
1084 paths.extend(df.keys())
1085 paths.extend(do)
1086
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301087 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001088 try:
1089 i = di[p]
1090 except KeyError:
1091 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001093 try:
1094 f = df[p]
1095 except KeyError:
1096 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +02001097
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001098 if i:
1099 i_status = i.status.upper()
1100 else:
1101 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001103 if f:
1104 f_status = f.status.lower()
1105 else:
1106 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001107
1108 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001109 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001110 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001111 else:
1112 line = ' %s%s\t%s' % (i_status, f_status, p)
1113
1114 if i and not f:
1115 out.added('%s', line)
1116 elif (i and f) or (not i and f):
1117 out.changed('%s', line)
1118 elif not i and not f:
1119 out.untracked('%s', line)
1120 else:
1121 out.write('%s', line)
1122 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001123
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001124 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001125
pelyad67872d2012-03-28 14:49:58 +03001126 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001127 """Prints the status of the repository to stdout.
1128 """
1129 out = DiffColoring(self.config)
1130 cmd = ['diff']
1131 if out.is_on:
1132 cmd.append('--color')
1133 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001134 if absolute_paths:
1135 cmd.append('--src-prefix=a/%s/' % self.relpath)
1136 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001137 cmd.append('--')
1138 p = GitCommand(self,
1139 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001140 capture_stdout=True,
1141 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001142 has_diff = False
1143 for line in p.process.stdout:
1144 if not has_diff:
1145 out.nl()
1146 out.project('project %s/' % self.relpath)
1147 out.nl()
1148 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001149 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001150 p.Wait()
1151
1152
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001153# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001154
David Pursehouse8a68ff92012-09-24 12:15:13 +09001155 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001156 """Was the branch published (uploaded) for code review?
1157 If so, returns the SHA-1 hash of the last published
1158 state for the branch.
1159 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001160 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001161 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001162 try:
1163 return self.bare_git.rev_parse(key)
1164 except GitError:
1165 return None
1166 else:
1167 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001168 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001169 except KeyError:
1170 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001171
David Pursehouse8a68ff92012-09-24 12:15:13 +09001172 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001173 """Prunes any stale published refs.
1174 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001175 if all_refs is None:
1176 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001177 heads = set()
1178 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301179 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001180 if name.startswith(R_HEADS):
1181 heads.add(name)
1182 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001183 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001184
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301185 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001186 n = name[len(R_PUB):]
1187 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001188 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001189
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001190 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001191 """List any branches which can be uploaded for review.
1192 """
1193 heads = {}
1194 pubed = {}
1195
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301196 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001197 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001198 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001199 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001200 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
1202 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301203 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001204 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001205 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001206 if selected_branch and branch != selected_branch:
1207 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001208
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001209 rb = self.GetUploadableBranch(branch)
1210 if rb:
1211 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001212 return ready
1213
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001214 def GetUploadableBranch(self, branch_name):
1215 """Get a single uploadable branch, or None.
1216 """
1217 branch = self.GetBranch(branch_name)
1218 base = branch.LocalMerge
1219 if branch.LocalMerge:
1220 rb = ReviewableBranch(self, branch, base)
1221 if rb.commits:
1222 return rb
1223 return None
1224
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001225 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001226 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001227 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001228 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001229 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001230 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001231 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001232 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001233 validate_certs=True,
1234 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001235 """Uploads the named branch for code review.
1236 """
1237 if branch is None:
1238 branch = self.CurrentBranch
1239 if branch is None:
1240 raise GitError('not currently on a branch')
1241
1242 branch = self.GetBranch(branch)
1243 if not branch.LocalMerge:
1244 raise GitError('branch %s does not track a remote' % branch.name)
1245 if not branch.remote.review:
1246 raise GitError('remote %s has no review url' % branch.remote.name)
1247
Bryan Jacobsf609f912013-05-06 13:36:24 -04001248 if dest_branch is None:
1249 dest_branch = self.dest_branch
1250 if dest_branch is None:
1251 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001252 if not dest_branch.startswith(R_HEADS):
1253 dest_branch = R_HEADS + dest_branch
1254
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001255 if not branch.remote.projectname:
1256 branch.remote.projectname = self.name
1257 branch.remote.Save()
1258
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001259 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001260 if url is None:
1261 raise UploadError('review not configured')
1262 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001263
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001264 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001265 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001266
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001267 for push_option in (push_options or []):
1268 cmd.append('-o')
1269 cmd.append(push_option)
1270
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001271 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001272
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001273 if dest_branch.startswith(R_HEADS):
1274 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001275
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001276 upload_type = 'for'
1277 if draft:
1278 upload_type = 'drafts'
1279
1280 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1281 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001282 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001283 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001284 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001285
David Pursehousef25a3702018-11-14 19:01:22 -08001286 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001287 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001288 if notify:
1289 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001290 if private:
1291 opts += ['private']
1292 if wip:
1293 opts += ['wip']
1294 if opts:
1295 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001296 cmd.append(ref_spec)
1297
Anthony King7bdac712014-07-16 12:56:40 +01001298 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001299 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001300
1301 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1302 self.bare_git.UpdateRef(R_PUB + branch.name,
1303 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001304 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001305
1306
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001307# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001308
Julien Campergue335f5ef2013-10-16 11:02:35 +02001309 def _ExtractArchive(self, tarpath, path=None):
1310 """Extract the given tar on its current location
1311
1312 Args:
1313 - tarpath: The path to the actual tar file
1314
1315 """
1316 try:
1317 with tarfile.open(tarpath, 'r') as tar:
1318 tar.extractall(path=path)
1319 return True
1320 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001321 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001322 return False
1323
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001324 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001325 quiet=False,
1326 is_new=None,
1327 current_branch_only=False,
1328 force_sync=False,
1329 clone_bundle=True,
1330 no_tags=False,
1331 archive=False,
1332 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001333 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001334 submodules=False,
1335 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001336 """Perform only the network IO portion of the sync process.
1337 Local working directory/branch state is not affected.
1338 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001339 if archive and not isinstance(self, MetaProject):
1340 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001341 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001342 return False
1343
1344 name = self.relpath.replace('\\', '/')
1345 name = name.replace('/', '_')
1346 tarpath = '%s.tar' % name
1347 topdir = self.manifest.topdir
1348
1349 try:
1350 self._FetchArchive(tarpath, cwd=topdir)
1351 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001352 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001353 return False
1354
1355 # From now on, we only need absolute tarpath
1356 tarpath = os.path.join(topdir, tarpath)
1357
1358 if not self._ExtractArchive(tarpath, path=topdir):
1359 return False
1360 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001361 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001362 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001363 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001364 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001365 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001366 if is_new is None:
1367 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001368 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001369 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001370 else:
1371 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001372 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001373
1374 if is_new:
1375 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1376 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001377 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001378 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001379 # This works for both absolute and relative alternate directories.
1380 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001381 finally:
1382 fd.close()
1383 except IOError:
1384 alt_dir = None
1385 else:
1386 alt_dir = None
1387
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001388 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001389 and alt_dir is None \
1390 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001391 is_new = False
1392
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001393 if not current_branch_only:
1394 if self.sync_c:
1395 current_branch_only = True
1396 elif not self.manifest._loaded:
1397 # Manifest cannot check defaults until it syncs.
1398 current_branch_only = False
1399 elif self.manifest.default.sync_c:
1400 current_branch_only = True
1401
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001402 if not no_tags:
1403 if not self.sync_tags:
1404 no_tags = True
1405
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001406 if self.clone_depth:
1407 depth = self.clone_depth
1408 else:
1409 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1410
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001411 need_to_fetch = not (optimized_fetch and
1412 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001413 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001414 if (need_to_fetch and
1415 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1416 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001417 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001418 submodules=submodules, force_sync=force_sync,
1419 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001420 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001421
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001422 mp = self.manifest.manifestProject
1423 dissociate = mp.config.GetBoolean('repo.dissociate')
1424 if dissociate:
1425 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1426 if os.path.exists(alternates_file):
1427 cmd = ['repack', '-a', '-d']
1428 if GitCommand(self, cmd, bare=True).Wait() != 0:
1429 return False
1430 platform_utils.remove(alternates_file)
1431
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001432 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001433 self._InitMRef()
1434 else:
1435 self._InitMirrorHead()
1436 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001437 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001438 except OSError:
1439 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001440 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001441
1442 def PostRepoUpgrade(self):
1443 self._InitHooks()
1444
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001445 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001446 if self.manifest.isGitcClient:
1447 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001448 for copyfile in self.copyfiles:
1449 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001450 for linkfile in self.linkfiles:
1451 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001452
Julien Camperguedd654222014-01-09 16:21:37 +01001453 def GetCommitRevisionId(self):
1454 """Get revisionId of a commit.
1455
1456 Use this method instead of GetRevisionId to get the id of the commit rather
1457 than the id of the current git object (for example, a tag)
1458
1459 """
1460 if not self.revisionExpr.startswith(R_TAGS):
1461 return self.GetRevisionId(self._allrefs)
1462
1463 try:
1464 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1465 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001466 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1467 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001468
David Pursehouse8a68ff92012-09-24 12:15:13 +09001469 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001470 if self.revisionId:
1471 return self.revisionId
1472
1473 rem = self.GetRemote(self.remote.name)
1474 rev = rem.ToLocal(self.revisionExpr)
1475
David Pursehouse8a68ff92012-09-24 12:15:13 +09001476 if all_refs is not None and rev in all_refs:
1477 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001478
1479 try:
1480 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1481 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001482 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1483 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001484
Martin Kellye4e94d22017-03-21 16:05:12 -07001485 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001486 """Perform only the local IO portion of the sync process.
1487 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001488 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001489 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001490 all_refs = self.bare_ref.all
1491 self.CleanPublishedCache(all_refs)
1492 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001493
David Pursehouse1d947b32012-10-25 12:23:11 +09001494 def _doff():
1495 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001496 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001497
Martin Kellye4e94d22017-03-21 16:05:12 -07001498 def _dosubmodules():
1499 self._SyncSubmodules(quiet=True)
1500
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001501 head = self.work_git.GetHead()
1502 if head.startswith(R_HEADS):
1503 branch = head[len(R_HEADS):]
1504 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001505 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001506 except KeyError:
1507 head = None
1508 else:
1509 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001510
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001511 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001512 # Currently on a detached HEAD. The user is assumed to
1513 # not have any local modifications worth worrying about.
1514 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001515 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001516 syncbuf.fail(self, _PriorSyncFailedError())
1517 return
1518
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001519 if head == revid:
1520 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001521 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001522 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001523 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001524 # The copy/linkfile config may have changed.
1525 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001526 return
1527 else:
1528 lost = self._revlist(not_rev(revid), HEAD)
1529 if lost:
1530 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001531
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001532 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001533 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001534 if submodules:
1535 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001536 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001537 syncbuf.fail(self, e)
1538 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001539 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001540 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001542 if head == revid:
1543 # No changes; don't do anything further.
1544 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001545 # The copy/linkfile config may have changed.
1546 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001547 return
1548
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001549 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001550
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001551 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001552 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001553 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001554 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001555 syncbuf.info(self,
1556 "leaving %s; does not track upstream",
1557 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001558 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001559 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001560 if submodules:
1561 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001562 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001563 syncbuf.fail(self, e)
1564 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001565 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001566 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001567
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001568 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001569 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001570 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001571 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001572 if not_merged:
1573 if upstream_gain:
1574 # The user has published this branch and some of those
1575 # commits are not yet merged upstream. We do not want
1576 # to rewrite the published commits so we punt.
1577 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001578 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001579 "branch %s is published (but not merged) and is now "
1580 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001581 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001582 elif pub == head:
1583 # All published commits are merged, and thus we are a
1584 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001585 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001586 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001587 if submodules:
1588 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001589 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001591 # Examine the local commits not in the remote. Find the
1592 # last one attributed to this user, if any.
1593 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001594 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001595 last_mine = None
1596 cnt_mine = 0
1597 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301598 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001599 if committer_email == self.UserEmail:
1600 last_mine = commit_id
1601 cnt_mine += 1
1602
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001603 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001604 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001605
1606 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001607 syncbuf.fail(self, _DirtyError())
1608 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001609
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001610 # If the upstream switched on us, warn the user.
1611 #
1612 if branch.merge != self.revisionExpr:
1613 if branch.merge and self.revisionExpr:
1614 syncbuf.info(self,
1615 'manifest switched %s...%s',
1616 branch.merge,
1617 self.revisionExpr)
1618 elif branch.merge:
1619 syncbuf.info(self,
1620 'manifest no longer tracks %s',
1621 branch.merge)
1622
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001623 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001624 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001625 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001626 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001627 syncbuf.info(self,
1628 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001629 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001630
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001631 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001632 if not ID_RE.match(self.revisionExpr):
1633 # in case of manifest sync the revisionExpr might be a SHA1
1634 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001635 if not branch.merge.startswith('refs/'):
1636 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001637 branch.Save()
1638
Mike Pontillod3153822012-02-28 11:53:24 -08001639 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001640 def _docopyandlink():
1641 self._CopyAndLinkFiles()
1642
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001643 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001644 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001645 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001646 if submodules:
1647 syncbuf.later2(self, _dosubmodules)
1648 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001649 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001650 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001651 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001652 if submodules:
1653 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001654 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001655 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001656 syncbuf.fail(self, e)
1657 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001658 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001659 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001660 if submodules:
1661 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001662
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001663 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001664 # dest should already be an absolute path, but src is project relative
1665 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001666 abssrc = os.path.join(self.worktree, src)
1667 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001668
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001669 def AddLinkFile(self, src, dest, absdest):
1670 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001671 # make src relative path to dest
1672 absdestdir = os.path.dirname(absdest)
1673 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001674 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001675
James W. Mills24c13082012-04-12 15:04:13 -05001676 def AddAnnotation(self, name, value, keep):
1677 self.annotations.append(_Annotation(name, value, keep))
1678
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001679 def DownloadPatchSet(self, change_id, patch_id):
1680 """Download a single patch set of a single change to FETCH_HEAD.
1681 """
1682 remote = self.GetRemote(self.remote.name)
1683
1684 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001685 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001686 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001687 if GitCommand(self, cmd, bare=True).Wait() != 0:
1688 return None
1689 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001690 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001691 change_id,
1692 patch_id,
1693 self.bare_git.rev_parse('FETCH_HEAD'))
1694
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001695
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001696# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001697
Simran Basib9a1b732015-08-20 12:19:28 -07001698 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001699 """Create a new branch off the manifest's revision.
1700 """
Simran Basib9a1b732015-08-20 12:19:28 -07001701 if not branch_merge:
1702 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001703 head = self.work_git.GetHead()
1704 if head == (R_HEADS + name):
1705 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001706
David Pursehouse8a68ff92012-09-24 12:15:13 +09001707 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001708 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001709 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001710 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001711 capture_stdout=True,
1712 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001713
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001714 branch = self.GetBranch(name)
1715 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001716 branch.merge = branch_merge
1717 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1718 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001719 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001720
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001721 if head.startswith(R_HEADS):
1722 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001723 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001724 except KeyError:
1725 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001726 if revid and head and revid == head:
1727 ref = os.path.join(self.gitdir, R_HEADS + name)
1728 try:
1729 os.makedirs(os.path.dirname(ref))
1730 except OSError:
1731 pass
1732 _lwrite(ref, '%s\n' % revid)
1733 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1734 'ref: %s%s\n' % (R_HEADS, name))
1735 branch.Save()
1736 return True
1737
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001738 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001739 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001740 capture_stdout=True,
1741 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001742 branch.Save()
1743 return True
1744 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001745
Wink Saville02d79452009-04-10 13:01:24 -07001746 def CheckoutBranch(self, name):
1747 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001748
1749 Args:
1750 name: The name of the branch to checkout.
1751
1752 Returns:
1753 True if the checkout succeeded; False if it didn't; None if the branch
1754 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001755 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001756 rev = R_HEADS + name
1757 head = self.work_git.GetHead()
1758 if head == rev:
1759 # Already on the branch
1760 #
1761 return True
Wink Saville02d79452009-04-10 13:01:24 -07001762
David Pursehouse8a68ff92012-09-24 12:15:13 +09001763 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001764 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001765 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001766 except KeyError:
1767 # Branch does not exist in this project
1768 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001769 return None
Wink Saville02d79452009-04-10 13:01:24 -07001770
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001771 if head.startswith(R_HEADS):
1772 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001773 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001774 except KeyError:
1775 head = None
1776
1777 if head == revid:
1778 # Same revision; just update HEAD to point to the new
1779 # target branch, but otherwise take no other action.
1780 #
1781 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1782 'ref: %s%s\n' % (R_HEADS, name))
1783 return True
1784
1785 return GitCommand(self,
1786 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001787 capture_stdout=True,
1788 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001789
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001790 def AbandonBranch(self, name):
1791 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001792
1793 Args:
1794 name: The name of the branch to abandon.
1795
1796 Returns:
1797 True if the abandon succeeded; False if it didn't; None if the branch
1798 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001799 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001800 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001801 all_refs = self.bare_ref.all
1802 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001803 # Doesn't exist
1804 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001805
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001806 head = self.work_git.GetHead()
1807 if head == rev:
1808 # We can't destroy the branch while we are sitting
1809 # on it. Switch to a detached HEAD.
1810 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001811 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001812
David Pursehouse8a68ff92012-09-24 12:15:13 +09001813 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001814 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001815 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1816 '%s\n' % revid)
1817 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001818 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001819
1820 return GitCommand(self,
1821 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001822 capture_stdout=True,
1823 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001824
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001825 def PruneHeads(self):
1826 """Prune any topic branches already merged into upstream.
1827 """
1828 cb = self.CurrentBranch
1829 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001830 left = self._allrefs
1831 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001832 if name.startswith(R_HEADS):
1833 name = name[len(R_HEADS):]
1834 if cb is None or name != cb:
1835 kill.append(name)
1836
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001837 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001838 if cb is not None \
1839 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001840 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001841 self.work_git.DetachHead(HEAD)
1842 kill.append(cb)
1843
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001844 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001845 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001846
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001847 try:
1848 self.bare_git.DetachHead(rev)
1849
1850 b = ['branch', '-d']
1851 b.extend(kill)
1852 b = GitCommand(self, b, bare=True,
1853 capture_stdout=True,
1854 capture_stderr=True)
1855 b.Wait()
1856 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001857 if ID_RE.match(old):
1858 self.bare_git.DetachHead(old)
1859 else:
1860 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001861 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001862
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001863 for branch in kill:
1864 if (R_HEADS + branch) not in left:
1865 self.CleanPublishedCache()
1866 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001867
1868 if cb and cb not in kill:
1869 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001870 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001871
1872 kept = []
1873 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001874 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001875 branch = self.GetBranch(branch)
1876 base = branch.LocalMerge
1877 if not base:
1878 base = rev
1879 kept.append(ReviewableBranch(self, branch, base))
1880 return kept
1881
1882
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001883# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001884
1885 def GetRegisteredSubprojects(self):
1886 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001887
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001888 def rec(subprojects):
1889 if not subprojects:
1890 return
1891 result.extend(subprojects)
1892 for p in subprojects:
1893 rec(p.subprojects)
1894 rec(self.subprojects)
1895 return result
1896
1897 def _GetSubmodules(self):
1898 # Unfortunately we cannot call `git submodule status --recursive` here
1899 # because the working tree might not exist yet, and it cannot be used
1900 # without a working tree in its current implementation.
1901
1902 def get_submodules(gitdir, rev):
1903 # Parse .gitmodules for submodule sub_paths and sub_urls
1904 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1905 if not sub_paths:
1906 return []
1907 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1908 # revision of submodule repository
1909 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1910 submodules = []
1911 for sub_path, sub_url in zip(sub_paths, sub_urls):
1912 try:
1913 sub_rev = sub_revs[sub_path]
1914 except KeyError:
1915 # Ignore non-exist submodules
1916 continue
1917 submodules.append((sub_rev, sub_path, sub_url))
1918 return submodules
1919
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001920 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1921 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001922
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001923 def parse_gitmodules(gitdir, rev):
1924 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1925 try:
Anthony King7bdac712014-07-16 12:56:40 +01001926 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1927 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001928 except GitError:
1929 return [], []
1930 if p.Wait() != 0:
1931 return [], []
1932
1933 gitmodules_lines = []
1934 fd, temp_gitmodules_path = tempfile.mkstemp()
1935 try:
1936 os.write(fd, p.stdout)
1937 os.close(fd)
1938 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001939 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1940 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001941 if p.Wait() != 0:
1942 return [], []
1943 gitmodules_lines = p.stdout.split('\n')
1944 except GitError:
1945 return [], []
1946 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001947 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001948
1949 names = set()
1950 paths = {}
1951 urls = {}
1952 for line in gitmodules_lines:
1953 if not line:
1954 continue
1955 m = re_path.match(line)
1956 if m:
1957 names.add(m.group(1))
1958 paths[m.group(1)] = m.group(2)
1959 continue
1960 m = re_url.match(line)
1961 if m:
1962 names.add(m.group(1))
1963 urls[m.group(1)] = m.group(2)
1964 continue
1965 names = sorted(names)
1966 return ([paths.get(name, '') for name in names],
1967 [urls.get(name, '') for name in names])
1968
1969 def git_ls_tree(gitdir, rev, paths):
1970 cmd = ['ls-tree', rev, '--']
1971 cmd.extend(paths)
1972 try:
Anthony King7bdac712014-07-16 12:56:40 +01001973 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1974 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001975 except GitError:
1976 return []
1977 if p.Wait() != 0:
1978 return []
1979 objects = {}
1980 for line in p.stdout.split('\n'):
1981 if not line.strip():
1982 continue
1983 object_rev, object_path = line.split()[2:4]
1984 objects[object_path] = object_rev
1985 return objects
1986
1987 try:
1988 rev = self.GetRevisionId()
1989 except GitError:
1990 return []
1991 return get_submodules(self.gitdir, rev)
1992
1993 def GetDerivedSubprojects(self):
1994 result = []
1995 if not self.Exists:
1996 # If git repo does not exist yet, querying its submodules will
1997 # mess up its states; so return here.
1998 return result
1999 for rev, path, url in self._GetSubmodules():
2000 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07002001 relpath, worktree, gitdir, objdir = \
2002 self.manifest.GetSubprojectPaths(self, name, path)
2003 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002004 if project:
2005 result.extend(project.GetDerivedSubprojects())
2006 continue
David James8d201162013-10-11 17:03:19 -07002007
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08002008 if url.startswith('..'):
2009 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002010 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01002011 url=url,
Steve Raed6480452016-08-10 15:00:00 -07002012 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01002013 review=self.remote.review,
2014 revision=self.remote.revision)
2015 subproject = Project(manifest=self.manifest,
2016 name=name,
2017 remote=remote,
2018 gitdir=gitdir,
2019 objdir=objdir,
2020 worktree=worktree,
2021 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02002022 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01002023 revisionId=rev,
2024 rebase=self.rebase,
2025 groups=self.groups,
2026 sync_c=self.sync_c,
2027 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09002028 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01002029 parent=self,
2030 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002031 result.append(subproject)
2032 result.extend(subproject.GetDerivedSubprojects())
2033 return result
2034
2035
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002036# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002037 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002038 try:
2039 # if revision (sha or tag) is not present then following function
2040 # throws an error.
2041 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2042 return True
2043 except GitError:
2044 # There is no such persistent revision. We have to fetch it.
2045 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002046
Julien Campergue335f5ef2013-10-16 11:02:35 +02002047 def _FetchArchive(self, tarpath, cwd=None):
2048 cmd = ['archive', '-v', '-o', tarpath]
2049 cmd.append('--remote=%s' % self.remote.url)
2050 cmd.append('--prefix=%s/' % self.relpath)
2051 cmd.append(self.revisionExpr)
2052
2053 command = GitCommand(self, cmd, cwd=cwd,
2054 capture_stdout=True,
2055 capture_stderr=True)
2056
2057 if command.Wait() != 0:
2058 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2059
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002060 def _RemoteFetch(self, name=None,
2061 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002062 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002063 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002064 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002065 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002066 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002067 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002068 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07002069 force_sync=False,
2070 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002071
2072 is_sha1 = False
2073 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002074 # The depth should not be used when fetching to a mirror because
2075 # it will result in a shallow repository that cannot be cloned or
2076 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002077 # The repo project should also never be synced with partial depth.
2078 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2079 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002080
Shawn Pearce69e04d82014-01-29 12:48:54 -08002081 if depth:
2082 current_branch_only = True
2083
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002084 if ID_RE.match(self.revisionExpr) is not None:
2085 is_sha1 = True
2086
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002088 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002089 # this is a tag and its sha1 value should never change
2090 tag_name = self.revisionExpr[len(R_TAGS):]
2091
2092 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002093 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002094 if not quiet:
2095 print('Skipped fetching project %s (already have persistent ref)'
2096 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002097 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002098 if is_sha1 and not depth:
2099 # When syncing a specific commit and --depth is not set:
2100 # * if upstream is explicitly specified and is not a sha1, fetch only
2101 # upstream as users expect only upstream to be fetch.
2102 # Note: The commit might not be in upstream in which case the sync
2103 # will fail.
2104 # * otherwise, fetch all branches to make sure we end up with the
2105 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002106 if self.upstream:
2107 current_branch_only = not ID_RE.match(self.upstream)
2108 else:
2109 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002110
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002111 if not name:
2112 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002113
2114 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002115 remote = self.GetRemote(name)
2116 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002117 ssh_proxy = True
2118
Shawn O. Pearce88443382010-10-08 10:02:09 +02002119 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002120 if alt_dir and 'objects' == os.path.basename(alt_dir):
2121 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002122 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2123 remote = self.GetRemote(name)
2124
David Pursehouse8a68ff92012-09-24 12:15:13 +09002125 all_refs = self.bare_ref.all
2126 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002127 tmp = set()
2128
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302129 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002130 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002131 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002132 all_refs[r] = ref_id
2133 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134 continue
2135
David Pursehouse8a68ff92012-09-24 12:15:13 +09002136 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002137 continue
2138
David Pursehouse8a68ff92012-09-24 12:15:13 +09002139 r = 'refs/_alt/%s' % ref_id
2140 all_refs[r] = ref_id
2141 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002142 tmp.add(r)
2143
heping3d7bbc92017-04-12 19:51:47 +08002144 tmp_packed_lines = []
2145 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002146
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302147 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002148 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002149 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002150 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002151 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002152
heping3d7bbc92017-04-12 19:51:47 +08002153 tmp_packed = ''.join(tmp_packed_lines)
2154 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002155 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002156 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002157 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002158
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002159 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002160
Xin Li745be2e2019-06-03 11:24:30 -07002161 if clone_filter:
2162 git_require((2, 19, 0), fail=True, msg='partial clones')
2163 cmd.append('--filter=%s' % clone_filter)
2164 self.config.SetString('extensions.partialclone', self.remote.name)
2165
Conley Owensf97e8382015-01-21 11:12:46 -08002166 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002167 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002168 else:
2169 # If this repo has shallow objects, then we don't know which refs have
2170 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2171 # do this with projects that don't have shallow objects, since it is less
2172 # efficient.
2173 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2174 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002175
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002176 if quiet:
2177 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002178 if not self.worktree:
2179 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002180 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002181
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002182 # If using depth then we should not get all the tags since they may
2183 # be outside of the depth.
2184 if no_tags or depth:
2185 cmd.append('--no-tags')
2186 else:
2187 cmd.append('--tags')
2188
Mike Frysingere57f1142019-03-18 21:27:54 -04002189 if force_sync:
2190 cmd.append('--force')
2191
David Pursehouse74cfd272015-10-14 10:50:15 +09002192 if prune:
2193 cmd.append('--prune')
2194
Martin Kellye4e94d22017-03-21 16:05:12 -07002195 if submodules:
2196 cmd.append('--recurse-submodules=on-demand')
2197
Conley Owens80b87fe2014-05-09 17:13:44 -07002198 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002199 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002200 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002201 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002202 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002203 spec.append('tag')
2204 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002205
David Pursehouse403b64e2015-04-27 10:41:33 +09002206 if not self.manifest.IsMirror:
2207 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002208 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002209 # Shallow checkout of a specific commit, fetch from that commit and not
2210 # the heads only as the commit might be deeper in the history.
2211 spec.append(branch)
2212 else:
2213 if is_sha1:
2214 branch = self.upstream
2215 if branch is not None and branch.strip():
2216 if not branch.startswith('refs/'):
2217 branch = R_HEADS + branch
2218 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002219 cmd.extend(spec)
2220
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002221 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002222 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002223 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002224 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002225 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002226 ok = True
2227 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002228 # If needed, run the 'git remote prune' the first time through the loop
2229 elif (not _i and
2230 "error:" in gitcmd.stderr and
2231 "git remote prune" in gitcmd.stderr):
2232 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002233 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002234 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002235 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002236 break
2237 continue
Brian Harring14a66742012-09-28 20:21:57 -07002238 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002239 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2240 # in sha1 mode, we just tried sync'ing from the upstream field; it
2241 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002242 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002243 elif ret < 0:
2244 # Git died with a signal, exit immediately
2245 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002246 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002247
2248 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002249 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002250 if old_packed != '':
2251 _lwrite(packed_refs, old_packed)
2252 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002253 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002254 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002255
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002256 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002257 # We just synced the upstream given branch; verify we
2258 # got what we wanted, else trigger a second run of all
2259 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002260 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002261 if current_branch_only and depth:
2262 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002263 return self._RemoteFetch(name=name,
2264 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002265 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002266 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002267 else:
2268 # Avoid infinite recursion: sync all branches with depth set to None
2269 return self._RemoteFetch(name=name, current_branch_only=False,
2270 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002271 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002272
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002273 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002274
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002275 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002276 if initial and \
2277 (self.manifest.manifestProject.config.GetString('repo.depth') or
2278 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002279 return False
2280
2281 remote = self.GetRemote(self.remote.name)
2282 bundle_url = remote.url + '/clone.bundle'
2283 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002284 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2285 'persistent-http',
2286 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002287 return False
2288
2289 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2290 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2291
2292 exist_dst = os.path.exists(bundle_dst)
2293 exist_tmp = os.path.exists(bundle_tmp)
2294
2295 if not initial and not exist_dst and not exist_tmp:
2296 return False
2297
2298 if not exist_dst:
2299 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2300 if not exist_dst:
2301 return False
2302
2303 cmd = ['fetch']
2304 if quiet:
2305 cmd.append('--quiet')
2306 if not self.worktree:
2307 cmd.append('--update-head-ok')
2308 cmd.append(bundle_dst)
2309 for f in remote.fetch:
2310 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002311 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002312
2313 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002314 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002315 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002316 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002317 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002318 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002319
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002320 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002321 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002322 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002323
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002324 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002325 if quiet:
2326 cmd += ['--silent']
2327 if os.path.exists(tmpPath):
2328 size = os.stat(tmpPath).st_size
2329 if size >= 1024:
2330 cmd += ['--continue-at', '%d' % (size,)]
2331 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002332 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002333 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002334 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002335 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002336 if proxy:
2337 cmd += ['--proxy', proxy]
2338 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2339 cmd += ['--proxy', os.environ['http_proxy']]
2340 if srcUrl.startswith('persistent-https'):
2341 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2342 elif srcUrl.startswith('persistent-http'):
2343 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002344 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002345
Dave Borowitz137d0132015-01-02 11:12:54 -08002346 if IsTrace():
2347 Trace('%s', ' '.join(cmd))
2348 try:
2349 proc = subprocess.Popen(cmd)
2350 except OSError:
2351 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002352
Dave Borowitz137d0132015-01-02 11:12:54 -08002353 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002354
Dave Borowitz137d0132015-01-02 11:12:54 -08002355 if curlret == 22:
2356 # From curl man page:
2357 # 22: HTTP page not retrieved. The requested url was not found or
2358 # returned another error with the HTTP error code being 400 or above.
2359 # This return code only appears if -f, --fail is used.
2360 if not quiet:
2361 print("Server does not provide clone.bundle; ignoring.",
2362 file=sys.stderr)
2363 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002364
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002365 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002366 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002367 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002368 return True
2369 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002370 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002371 return False
2372 else:
2373 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002374
Kris Giesingc8d882a2014-12-23 13:02:32 -08002375 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002376 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002377 with open(path, 'rb') as f:
2378 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002379 return True
2380 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002381 if not quiet:
2382 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002383 return False
2384 except OSError:
2385 return False
2386
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002387 def _Checkout(self, rev, quiet=False):
2388 cmd = ['checkout']
2389 if quiet:
2390 cmd.append('-q')
2391 cmd.append(rev)
2392 cmd.append('--')
2393 if GitCommand(self, cmd).Wait() != 0:
2394 if self._allrefs:
2395 raise GitError('%s checkout %s ' % (self.name, rev))
2396
Anthony King7bdac712014-07-16 12:56:40 +01002397 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002398 cmd = ['cherry-pick']
2399 cmd.append(rev)
2400 cmd.append('--')
2401 if GitCommand(self, cmd).Wait() != 0:
2402 if self._allrefs:
2403 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2404
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302405 def _LsRemote(self, refs):
2406 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302407 p = GitCommand(self, cmd, capture_stdout=True)
2408 if p.Wait() == 0:
2409 if hasattr(p.stdout, 'decode'):
2410 return p.stdout.decode('utf-8')
2411 else:
2412 return p.stdout
2413 return None
2414
Anthony King7bdac712014-07-16 12:56:40 +01002415 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002416 cmd = ['revert']
2417 cmd.append('--no-edit')
2418 cmd.append(rev)
2419 cmd.append('--')
2420 if GitCommand(self, cmd).Wait() != 0:
2421 if self._allrefs:
2422 raise GitError('%s revert %s ' % (self.name, rev))
2423
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002424 def _ResetHard(self, rev, quiet=True):
2425 cmd = ['reset', '--hard']
2426 if quiet:
2427 cmd.append('-q')
2428 cmd.append(rev)
2429 if GitCommand(self, cmd).Wait() != 0:
2430 raise GitError('%s reset --hard %s ' % (self.name, rev))
2431
Martin Kellye4e94d22017-03-21 16:05:12 -07002432 def _SyncSubmodules(self, quiet=True):
2433 cmd = ['submodule', 'update', '--init', '--recursive']
2434 if quiet:
2435 cmd.append('-q')
2436 if GitCommand(self, cmd).Wait() != 0:
2437 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2438
Anthony King7bdac712014-07-16 12:56:40 +01002439 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002440 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002441 if onto is not None:
2442 cmd.extend(['--onto', onto])
2443 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002444 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002445 raise GitError('%s rebase %s ' % (self.name, upstream))
2446
Pierre Tardy3d125942012-05-04 12:18:12 +02002447 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002448 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002449 if ffonly:
2450 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002451 if GitCommand(self, cmd).Wait() != 0:
2452 raise GitError('%s merge %s ' % (self.name, head))
2453
Kevin Degiabaa7f32014-11-12 11:27:45 -07002454 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002455 init_git_dir = not os.path.exists(self.gitdir)
2456 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002457 try:
2458 # Initialize the bare repository, which contains all of the objects.
2459 if init_obj_dir:
2460 os.makedirs(self.objdir)
2461 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002462
Kevin Degib1a07b82015-07-27 13:33:43 -06002463 # If we have a separate directory to hold refs, initialize it as well.
2464 if self.objdir != self.gitdir:
2465 if init_git_dir:
2466 os.makedirs(self.gitdir)
2467
2468 if init_obj_dir or init_git_dir:
2469 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2470 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002471 try:
2472 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2473 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002474 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002475 print("Retrying clone after deleting %s" %
2476 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002477 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002478 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2479 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002480 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002481 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002482 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2483 except:
2484 raise e
2485 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002486
Kevin Degi384b3c52014-10-16 16:02:58 -06002487 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002488 mp = self.manifest.manifestProject
2489 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002490
Kevin Degib1a07b82015-07-27 13:33:43 -06002491 if ref_dir or mirror_git:
2492 if not mirror_git:
2493 mirror_git = os.path.join(ref_dir, self.name + '.git')
2494 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2495 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002496
Kevin Degib1a07b82015-07-27 13:33:43 -06002497 if os.path.exists(mirror_git):
2498 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002499
Kevin Degib1a07b82015-07-27 13:33:43 -06002500 elif os.path.exists(repo_git):
2501 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002502
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 else:
2504 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002505
Kevin Degib1a07b82015-07-27 13:33:43 -06002506 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002507 if not os.path.isabs(ref_dir):
2508 # The alternate directory is relative to the object database.
2509 ref_dir = os.path.relpath(ref_dir,
2510 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002511 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2512 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002513
Kevin Degib1a07b82015-07-27 13:33:43 -06002514 self._UpdateHooks()
2515
2516 m = self.manifest.manifestProject.config
2517 for key in ['user.name', 'user.email']:
2518 if m.Has(key, include_defaults=False):
2519 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002520 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002521 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002522 if self.manifest.IsMirror:
2523 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002524 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002525 self.config.SetString('core.bare', None)
2526 except Exception:
2527 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002528 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002529 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002530 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002531 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002532
Jimmie Westera0444582012-10-24 13:44:42 +02002533 def _UpdateHooks(self):
2534 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002535 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002536
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002537 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002538 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002539 if not os.path.exists(hooks):
2540 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002541 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002542 name = os.path.basename(stock_hook)
2543
Victor Boivie65e0f352011-04-18 11:23:29 +02002544 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002545 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002546 # Don't install a Gerrit Code Review hook if this
2547 # project does not appear to use it for reviews.
2548 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002549 # Since the manifest project is one of those, but also
2550 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002551 continue
2552
2553 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002554 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002555 continue
2556 if os.path.exists(dst):
2557 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002558 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002559 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002560 _warn("%s: Not replacing locally modified %s hook",
2561 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002562 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002563 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002564 platform_utils.symlink(
2565 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002566 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002567 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002568 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002569 else:
2570 raise
2571
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002572 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002573 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002574 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002575 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002576 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002577 remote.review = self.remote.review
2578 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002579
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002580 if self.worktree:
2581 remote.ResetFetch(mirror=False)
2582 else:
2583 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002584 remote.Save()
2585
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002586 def _InitMRef(self):
2587 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002588 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002589
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002590 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002591 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002592
2593 def _InitAnyMRef(self, ref):
2594 cur = self.bare_ref.symref(ref)
2595
2596 if self.revisionId:
2597 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2598 msg = 'manifest set to %s' % self.revisionId
2599 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002600 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002601 else:
2602 remote = self.GetRemote(self.remote.name)
2603 dst = remote.ToLocal(self.revisionExpr)
2604 if cur != dst:
2605 msg = 'manifest set to %s' % self.revisionExpr
2606 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002607
Kevin Degi384b3c52014-10-16 16:02:58 -06002608 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002609 symlink_files = self.shareable_files[:]
2610 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002611 if share_refs:
2612 symlink_files += self.working_tree_files
2613 symlink_dirs += self.working_tree_dirs
2614 to_symlink = symlink_files + symlink_dirs
2615 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002616 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002617 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002618 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002619 # Fail if the links are pointing to the wrong place
2620 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002621 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002622 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002623 'work tree. If you\'re comfortable with the '
2624 'possibility of losing the work tree\'s git metadata,'
2625 ' use `repo sync --force-sync {0}` to '
2626 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002627
David James8d201162013-10-11 17:03:19 -07002628 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2629 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2630
2631 Args:
2632 gitdir: The bare git repository. Must already be initialized.
2633 dotgit: The repository you would like to initialize.
2634 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2635 Only one work tree can store refs under a given |gitdir|.
2636 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2637 This saves you the effort of initializing |dotgit| yourself.
2638 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002639 symlink_files = self.shareable_files[:]
2640 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002641 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002642 symlink_files += self.working_tree_files
2643 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002644 to_symlink = symlink_files + symlink_dirs
2645
2646 to_copy = []
2647 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002648 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002649
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002650 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002651 for name in set(to_copy).union(to_symlink):
2652 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002653 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002654 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002655
Kevin Degi384b3c52014-10-16 16:02:58 -06002656 if os.path.lexists(dst):
2657 continue
David James8d201162013-10-11 17:03:19 -07002658
2659 # If the source dir doesn't exist, create an empty dir.
2660 if name in symlink_dirs and not os.path.lexists(src):
2661 os.makedirs(src)
2662
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002663 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002664 platform_utils.symlink(
2665 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002666 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002667 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002668 shutil.copytree(src, dst)
2669 elif os.path.isfile(src):
2670 shutil.copy(src, dst)
2671
Conley Owens80b87fe2014-05-09 17:13:44 -07002672 # If the source file doesn't exist, ensure the destination
2673 # file doesn't either.
2674 if name in symlink_files and not os.path.lexists(src):
2675 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002676 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002677 except OSError:
2678 pass
2679
David James8d201162013-10-11 17:03:19 -07002680 except OSError as e:
2681 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002682 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002683 else:
2684 raise
2685
Martin Kellye4e94d22017-03-21 16:05:12 -07002686 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002687 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002688 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002689 try:
2690 if init_dotgit:
2691 os.makedirs(dotgit)
2692 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2693 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002694
Kevin Degiabaa7f32014-11-12 11:27:45 -07002695 try:
2696 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2697 except GitError as e:
2698 if force_sync:
2699 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002700 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002701 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002702 except:
2703 raise e
2704 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002705
Kevin Degib1a07b82015-07-27 13:33:43 -06002706 if init_dotgit:
2707 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002708
Kevin Degib1a07b82015-07-27 13:33:43 -06002709 cmd = ['read-tree', '--reset', '-u']
2710 cmd.append('-v')
2711 cmd.append(HEAD)
2712 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002713 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002714
Martin Kellye4e94d22017-03-21 16:05:12 -07002715 if submodules:
2716 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002717 self._CopyAndLinkFiles()
2718 except Exception:
2719 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002720 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002721 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002722
Renaud Paquay788e9622017-01-27 11:41:12 -08002723 def _get_symlink_error_message(self):
2724 if platform_utils.isWindows():
2725 return ('Unable to create symbolic link. Please re-run the command as '
2726 'Administrator, or see '
2727 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2728 'for other options.')
2729 return 'filesystem must support symlinks'
2730
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002731 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002732 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002733
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002734 def _revlist(self, *args, **kw):
2735 a = []
2736 a.extend(args)
2737 a.append('--')
2738 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002739
2740 @property
2741 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002742 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002743
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002744 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002745 """Get logs between two revisions of this project."""
2746 comp = '..'
2747 if rev1:
2748 revs = [rev1]
2749 if rev2:
2750 revs.extend([comp, rev2])
2751 cmd = ['log', ''.join(revs)]
2752 out = DiffColoring(self.config)
2753 if out.is_on and color:
2754 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002755 if pretty_format is not None:
2756 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002757 if oneline:
2758 cmd.append('--oneline')
2759
2760 try:
2761 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2762 if log.Wait() == 0:
2763 return log.stdout
2764 except GitError:
2765 # worktree may not exist if groups changed for example. In that case,
2766 # try in gitdir instead.
2767 if not os.path.exists(self.worktree):
2768 return self.bare_git.log(*cmd[1:])
2769 else:
2770 raise
2771 return None
2772
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002773 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2774 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002775 """Get the list of logs from this revision to given revisionId"""
2776 logs = {}
2777 selfId = self.GetRevisionId(self._allrefs)
2778 toId = toProject.GetRevisionId(toProject._allrefs)
2779
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002780 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2781 pretty_format=pretty_format)
2782 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2783 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002784 return logs
2785
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002786 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002787
David James8d201162013-10-11 17:03:19 -07002788 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002789 self._project = project
2790 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002791 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002792
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002793 def LsOthers(self):
2794 p = GitCommand(self._project,
2795 ['ls-files',
2796 '-z',
2797 '--others',
2798 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002799 bare=False,
David James8d201162013-10-11 17:03:19 -07002800 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002801 capture_stdout=True,
2802 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002803 if p.Wait() == 0:
2804 out = p.stdout
2805 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002806 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002807 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808 return []
2809
2810 def DiffZ(self, name, *args):
2811 cmd = [name]
2812 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002813 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814 cmd.extend(args)
2815 p = GitCommand(self._project,
2816 cmd,
David James8d201162013-10-11 17:03:19 -07002817 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002818 bare=False,
2819 capture_stdout=True,
2820 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002821 try:
2822 out = p.process.stdout.read()
2823 r = {}
2824 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002825 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002826 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002827 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002828 info = next(out)
2829 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002830 except StopIteration:
2831 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002832
2833 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002834
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002835 def __init__(self, path, omode, nmode, oid, nid, state):
2836 self.path = path
2837 self.src_path = None
2838 self.old_mode = omode
2839 self.new_mode = nmode
2840 self.old_id = oid
2841 self.new_id = nid
2842
2843 if len(state) == 1:
2844 self.status = state
2845 self.level = None
2846 else:
2847 self.status = state[:1]
2848 self.level = state[1:]
2849 while self.level.startswith('0'):
2850 self.level = self.level[1:]
2851
2852 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002853 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002854 if info.status in ('R', 'C'):
2855 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002856 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 r[info.path] = info
2858 return r
2859 finally:
2860 p.Wait()
2861
2862 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002863 if self._bare:
2864 path = os.path.join(self._project.gitdir, HEAD)
2865 else:
2866 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002867 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002868 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002869 except IOError as e:
2870 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002871 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002872 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002873 finally:
2874 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302875 try:
2876 line = line.decode()
2877 except AttributeError:
2878 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002879 if line.startswith('ref: '):
2880 return line[5:-1]
2881 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002882
2883 def SetHead(self, ref, message=None):
2884 cmdv = []
2885 if message is not None:
2886 cmdv.extend(['-m', message])
2887 cmdv.append(HEAD)
2888 cmdv.append(ref)
2889 self.symbolic_ref(*cmdv)
2890
2891 def DetachHead(self, new, message=None):
2892 cmdv = ['--no-deref']
2893 if message is not None:
2894 cmdv.extend(['-m', message])
2895 cmdv.append(HEAD)
2896 cmdv.append(new)
2897 self.update_ref(*cmdv)
2898
2899 def UpdateRef(self, name, new, old=None,
2900 message=None,
2901 detach=False):
2902 cmdv = []
2903 if message is not None:
2904 cmdv.extend(['-m', message])
2905 if detach:
2906 cmdv.append('--no-deref')
2907 cmdv.append(name)
2908 cmdv.append(new)
2909 if old is not None:
2910 cmdv.append(old)
2911 self.update_ref(*cmdv)
2912
2913 def DeleteRef(self, name, old=None):
2914 if not old:
2915 old = self.rev_parse(name)
2916 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002917 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002918
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002919 def rev_list(self, *args, **kw):
2920 if 'format' in kw:
2921 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2922 else:
2923 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002924 cmdv.extend(args)
2925 p = GitCommand(self._project,
2926 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002927 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002928 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002929 capture_stdout=True,
2930 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002931 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002932 raise GitError('%s rev-list %s: %s' %
2933 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002934 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002935
2936 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002937 """Allow arbitrary git commands using pythonic syntax.
2938
2939 This allows you to do things like:
2940 git_obj.rev_parse('HEAD')
2941
2942 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2943 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002944 Any other positional arguments will be passed to the git command, and the
2945 following keyword arguments are supported:
2946 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002947
2948 Args:
2949 name: The name of the git command to call. Any '_' characters will
2950 be replaced with '-'.
2951
2952 Returns:
2953 A callable object that will try to call git with the named command.
2954 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002955 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002956
Dave Borowitz091f8932012-10-23 17:01:04 -07002957 def runner(*args, **kwargs):
2958 cmdv = []
2959 config = kwargs.pop('config', None)
2960 for k in kwargs:
2961 raise TypeError('%s() got an unexpected keyword argument %r'
2962 % (name, k))
2963 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002964 if not git_require((1, 7, 2)):
2965 raise ValueError('cannot set config on command line for %s()'
2966 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302967 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002968 cmdv.append('-c')
2969 cmdv.append('%s=%s' % (k, v))
2970 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002971 cmdv.extend(args)
2972 p = GitCommand(self._project,
2973 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002974 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002975 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002976 capture_stdout=True,
2977 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002978 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002979 raise GitError('%s %s: %s' %
2980 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002981 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302982 try:
Conley Owensedd01512013-09-26 12:59:58 -07002983 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302984 except AttributeError:
2985 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002986 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2987 return r[:-1]
2988 return r
2989 return runner
2990
2991
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002992class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002993
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002994 def __str__(self):
2995 return 'prior sync failed; rebase still in progress'
2996
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002997
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002998class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002999
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003000 def __str__(self):
3001 return 'contains uncommitted changes'
3002
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003003
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003005
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003006 def __init__(self, project, text):
3007 self.project = project
3008 self.text = text
3009
3010 def Print(self, syncbuf):
3011 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3012 syncbuf.out.nl()
3013
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003014
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003015class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003016
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003017 def __init__(self, project, why):
3018 self.project = project
3019 self.why = why
3020
3021 def Print(self, syncbuf):
3022 syncbuf.out.fail('error: %s/: %s',
3023 self.project.relpath,
3024 str(self.why))
3025 syncbuf.out.nl()
3026
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003027
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003028class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003029
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003030 def __init__(self, project, action):
3031 self.project = project
3032 self.action = action
3033
3034 def Run(self, syncbuf):
3035 out = syncbuf.out
3036 out.project('project %s/', self.project.relpath)
3037 out.nl()
3038 try:
3039 self.action()
3040 out.nl()
3041 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003042 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003043 out.nl()
3044 return False
3045
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003046
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003047class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003048
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003049 def __init__(self, config):
3050 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003051 self.project = self.printer('header', attr='bold')
3052 self.info = self.printer('info')
3053 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003055
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003056class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003057
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003058 def __init__(self, config, detach_head=False):
3059 self._messages = []
3060 self._failures = []
3061 self._later_queue1 = []
3062 self._later_queue2 = []
3063
3064 self.out = _SyncColoring(config)
3065 self.out.redirect(sys.stderr)
3066
3067 self.detach_head = detach_head
3068 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003069 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003070
3071 def info(self, project, fmt, *args):
3072 self._messages.append(_InfoMessage(project, fmt % args))
3073
3074 def fail(self, project, err=None):
3075 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003076 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003077
3078 def later1(self, project, what):
3079 self._later_queue1.append(_Later(project, what))
3080
3081 def later2(self, project, what):
3082 self._later_queue2.append(_Later(project, what))
3083
3084 def Finish(self):
3085 self._PrintMessages()
3086 self._RunLater()
3087 self._PrintMessages()
3088 return self.clean
3089
David Rileye0684ad2017-04-05 00:02:59 -07003090 def Recently(self):
3091 recent_clean = self.recent_clean
3092 self.recent_clean = True
3093 return recent_clean
3094
3095 def _MarkUnclean(self):
3096 self.clean = False
3097 self.recent_clean = False
3098
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003099 def _RunLater(self):
3100 for q in ['_later_queue1', '_later_queue2']:
3101 if not self._RunQueue(q):
3102 return
3103
3104 def _RunQueue(self, queue):
3105 for m in getattr(self, queue):
3106 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003107 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003108 return False
3109 setattr(self, queue, [])
3110 return True
3111
3112 def _PrintMessages(self):
3113 for m in self._messages:
3114 m.Print(self)
3115 for m in self._failures:
3116 m.Print(self)
3117
3118 self._messages = []
3119 self._failures = []
3120
3121
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003122class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003123
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003124 """A special project housed under .repo.
3125 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003126
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003127 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003128 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003129 manifest=manifest,
3130 name=name,
3131 gitdir=gitdir,
3132 objdir=gitdir,
3133 worktree=worktree,
3134 remote=RemoteSpec('origin'),
3135 relpath='.repo/%s' % name,
3136 revisionExpr='refs/heads/master',
3137 revisionId=None,
3138 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003139
3140 def PreSync(self):
3141 if self.Exists:
3142 cb = self.CurrentBranch
3143 if cb:
3144 base = self.GetBranch(cb).merge
3145 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003146 self.revisionExpr = base
3147 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003148
Martin Kelly224a31a2017-07-10 14:46:25 -07003149 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003150 """ Prepare MetaProject for manifest branch switch
3151 """
3152
3153 # detach and delete manifest branch, allowing a new
3154 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003155 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003156 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003157 syncbuf.Finish()
3158
3159 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003160 ['update-ref', '-d', 'refs/heads/default'],
3161 capture_stdout=True,
3162 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003163
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003164 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003165 def LastFetch(self):
3166 try:
3167 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3168 return os.path.getmtime(fh)
3169 except OSError:
3170 return 0
3171
3172 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003173 def HasChanges(self):
3174 """Has the remote received new commits not yet checked out?
3175 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003176 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003177 return False
3178
David Pursehouse8a68ff92012-09-24 12:15:13 +09003179 all_refs = self.bare_ref.all
3180 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003181 head = self.work_git.GetHead()
3182 if head.startswith(R_HEADS):
3183 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003184 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003185 except KeyError:
3186 head = None
3187
3188 if revid == head:
3189 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003190 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003191 return True
3192 return False