blob: b41a57cfa9b3fcb6fc7a6f2600a6e90d2addc26e [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
Mike Frysinger70d861f2019-08-26 15:22:36 -040042import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040043from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044
Shawn O. Pearced237b692009-04-17 18:49:50 -070045from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070046
David Pursehouse59bbb582013-05-17 10:49:33 +090047from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040048if is_python3():
49 import urllib.parse
50else:
51 import imp
52 import urlparse
53 urllib = imp.new_module('urllib')
54 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053055 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053056
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070057
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070058def _lwrite(path, content):
59 lock = '%s.lock' % path
60
Chirayu Desai303a82f2014-08-19 22:57:17 +053061 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062 try:
63 fd.write(content)
64 finally:
65 fd.close()
66
67 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070068 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080070 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070071 raise
72
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070073
Shawn O. Pearce48244782009-04-16 08:25:57 -070074def _error(fmt, *args):
75 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070076 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070077
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070078
David Pursehousef33929d2015-08-24 14:39:14 +090079def _warn(fmt, *args):
80 msg = fmt % args
81 print('warn: %s' % msg, file=sys.stderr)
82
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070084def not_rev(r):
85 return '^' + r
86
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070087
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080088def sq(r):
89 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080090
Jonathan Nieder93719792015-03-17 11:29:58 -070091_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070092
93
Jonathan Nieder93719792015-03-17 11:29:58 -070094def _ProjectHooks():
95 """List the hooks present in the 'hooks' directory.
96
97 These hooks are project hooks and are copied to the '.git/hooks' directory
98 of all subprojects.
99
100 This function caches the list of hooks (based on the contents of the
101 'repo/hooks' directory) on the first call.
102
103 Returns:
104 A list of absolute paths to all of the files in the hooks directory.
105 """
106 global _project_hook_list
107 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700108 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700109 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700110 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700111 return _project_hook_list
112
113
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700114class DownloadedChange(object):
115 _commit_cache = None
116
117 def __init__(self, project, base, change_id, ps_id, commit):
118 self.project = project
119 self.base = base
120 self.change_id = change_id
121 self.ps_id = ps_id
122 self.commit = commit
123
124 @property
125 def commits(self):
126 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700127 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
128 '--abbrev-commit',
129 '--pretty=oneline',
130 '--reverse',
131 '--date-order',
132 not_rev(self.base),
133 self.commit,
134 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700135 return self._commit_cache
136
137
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138class ReviewableBranch(object):
139 _commit_cache = None
140
141 def __init__(self, project, branch, base):
142 self.project = project
143 self.branch = branch
144 self.base = base
145
146 @property
147 def name(self):
148 return self.branch.name
149
150 @property
151 def commits(self):
152 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700153 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
154 '--abbrev-commit',
155 '--pretty=oneline',
156 '--reverse',
157 '--date-order',
158 not_rev(self.base),
159 R_HEADS + self.name,
160 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700161 return self._commit_cache
162
163 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800164 def unabbrev_commits(self):
165 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700166 for commit in self.project.bare_git.rev_list(not_rev(self.base),
167 R_HEADS + self.name,
168 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800169 r[commit[0:8]] = commit
170 return r
171
172 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700173 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700174 return self.project.bare_git.log('--pretty=format:%cd',
175 '-n', '1',
176 R_HEADS + self.name,
177 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700179 def UploadForReview(self, people,
180 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000181 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200182 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700183 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200184 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200185 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800186 validate_certs=True,
187 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800188 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700189 people,
Brian Harring435370c2012-07-28 15:37:04 -0700190 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000191 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200192 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700193 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200194 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200195 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800196 validate_certs=validate_certs,
197 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700199 def GetPublishedRefs(self):
200 refs = {}
201 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700202 self.branch.remote.SshReviewUrl(self.project.UserEmail),
203 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700204 for line in output.split('\n'):
205 try:
206 (sha, ref) = line.split()
207 refs[sha] = ref
208 except ValueError:
209 pass
210
211 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700213
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700215
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216 def __init__(self, config):
217 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100218 self.project = self.printer('header', attr='bold')
219 self.branch = self.printer('header', attr='bold')
220 self.nobranch = self.printer('nobranch', fg='red')
221 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
Anthony King7bdac712014-07-16 12:56:40 +0100223 self.added = self.printer('added', fg='green')
224 self.changed = self.printer('changed', fg='red')
225 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226
227
228class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700229
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230 def __init__(self, config):
231 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100232 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400233 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
Anthony King7bdac712014-07-16 12:56:40 +0100236class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700237
James W. Mills24c13082012-04-12 15:04:13 -0500238 def __init__(self, name, value, keep):
239 self.name = name
240 self.value = value
241 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700243
Anthony King7bdac712014-07-16 12:56:40 +0100244class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700245
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800246 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247 self.src = src
248 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800249 self.abs_src = abssrc
250 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251
252 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800253 src = self.abs_src
254 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255 # copy file if it does not exist or is out of date
256 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
257 try:
258 # remove existing file first, since it might be read-only
259 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800260 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400261 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200262 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700263 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200264 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700265 shutil.copy(src, dest)
266 # make the file read-only
267 mode = os.stat(dest)[stat.ST_MODE]
268 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
269 os.chmod(dest, mode)
270 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700271 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700273
Anthony King7bdac712014-07-16 12:56:40 +0100274class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700275
Wink Saville4c426ef2015-06-03 08:05:17 -0700276 def __init__(self, git_worktree, src, dest, relsrc, absdest):
277 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500278 self.src = src
279 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700280 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 self.abs_dest = absdest
282
Wink Saville4c426ef2015-06-03 08:05:17 -0700283 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500284 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700285 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 try:
287 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800288 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800289 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700291 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700292 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500293 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700294 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500295 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700296 _error('Cannot link file %s to %s', relSrc, absDest)
297
298 def _Link(self):
299 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
300 on the src linking all of the files in the source in to the destination
301 directory.
302 """
303 # We use the absSrc to handle the situation where the current directory
304 # is not the root of the repo
305 absSrc = os.path.join(self.git_worktree, self.src)
306 if os.path.exists(absSrc):
307 # Entity exists so just a simple one to one link operation
308 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
309 else:
310 # Entity doesn't exist assume there is a wild card
311 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700312 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700313 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700314 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700315 else:
316 absSrcFiles = glob.glob(absSrc)
317 for absSrcFile in absSrcFiles:
318 # Create a releative path from source dir to destination dir
319 absSrcDir = os.path.dirname(absSrcFile)
320 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
321
322 # Get the source file name
323 srcFile = os.path.basename(absSrcFile)
324
325 # Now form the final full paths to srcFile. They will be
326 # absolute for the desintaiton and relative for the srouce.
327 absDest = os.path.join(absDestDir, srcFile)
328 relSrc = os.path.join(relSrcDir, srcFile)
329 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500330
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700331
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700332class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700333
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700334 def __init__(self,
335 name,
Anthony King7bdac712014-07-16 12:56:40 +0100336 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700337 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100338 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700339 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700340 orig_name=None,
341 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700342 self.name = name
343 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700344 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700345 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100346 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700347 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700348 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700349
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700350
Doug Anderson37282b42011-03-04 11:54:18 -0800351class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700352
Doug Anderson37282b42011-03-04 11:54:18 -0800353 """A RepoHook contains information about a script to run as a hook.
354
355 Hooks are used to run a python script before running an upload (for instance,
356 to run presubmit checks). Eventually, we may have hooks for other actions.
357
358 This shouldn't be confused with files in the 'repo/hooks' directory. Those
359 files are copied into each '.git/hooks' folder for each project. Repo-level
360 hooks are associated instead with repo actions.
361
362 Hooks are always python. When a hook is run, we will load the hook into the
363 interpreter and execute its main() function.
364 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700365
Doug Anderson37282b42011-03-04 11:54:18 -0800366 def __init__(self,
367 hook_type,
368 hooks_project,
369 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400370 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800371 abort_if_user_denies=False):
372 """RepoHook constructor.
373
374 Params:
375 hook_type: A string representing the type of hook. This is also used
376 to figure out the name of the file containing the hook. For
377 example: 'pre-upload'.
378 hooks_project: The project containing the repo hooks. If you have a
379 manifest, this is manifest.repo_hooks_project. OK if this is None,
380 which will make the hook a no-op.
381 topdir: Repo's top directory (the one containing the .repo directory).
382 Scripts will run with CWD as this directory. If you have a manifest,
383 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400384 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800385 abort_if_user_denies: If True, we'll throw a HookError() if the user
386 doesn't allow us to run the hook.
387 """
388 self._hook_type = hook_type
389 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400390 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800391 self._topdir = topdir
392 self._abort_if_user_denies = abort_if_user_denies
393
394 # Store the full path to the script for convenience.
395 if self._hooks_project:
396 self._script_fullpath = os.path.join(self._hooks_project.worktree,
397 self._hook_type + '.py')
398 else:
399 self._script_fullpath = None
400
401 def _GetHash(self):
402 """Return a hash of the contents of the hooks directory.
403
404 We'll just use git to do this. This hash has the property that if anything
405 changes in the directory we will return a different has.
406
407 SECURITY CONSIDERATION:
408 This hash only represents the contents of files in the hook directory, not
409 any other files imported or called by hooks. Changes to imported files
410 can change the script behavior without affecting the hash.
411
412 Returns:
413 A string representing the hash. This will always be ASCII so that it can
414 be printed to the user easily.
415 """
416 assert self._hooks_project, "Must have hooks to calculate their hash."
417
418 # We will use the work_git object rather than just calling GetRevisionId().
419 # That gives us a hash of the latest checked in version of the files that
420 # the user will actually be executing. Specifically, GetRevisionId()
421 # doesn't appear to change even if a user checks out a different version
422 # of the hooks repo (via git checkout) nor if a user commits their own revs.
423 #
424 # NOTE: Local (non-committed) changes will not be factored into this hash.
425 # I think this is OK, since we're really only worried about warning the user
426 # about upstream changes.
427 return self._hooks_project.work_git.rev_parse('HEAD')
428
429 def _GetMustVerb(self):
430 """Return 'must' if the hook is required; 'should' if not."""
431 if self._abort_if_user_denies:
432 return 'must'
433 else:
434 return 'should'
435
436 def _CheckForHookApproval(self):
437 """Check to see whether this hook has been approved.
438
Mike Frysinger40252c22016-08-15 21:23:44 -0400439 We'll accept approval of manifest URLs if they're using secure transports.
440 This way the user can say they trust the manifest hoster. For insecure
441 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800442
443 Note that we ask permission for each individual hook even though we use
444 the hash of all hooks when detecting changes. We'd like the user to be
445 able to approve / deny each hook individually. We only use the hash of all
446 hooks because there is no other easy way to detect changes to local imports.
447
448 Returns:
449 True if this hook is approved to run; False otherwise.
450
451 Raises:
452 HookError: Raised if the user doesn't approve and abort_if_user_denies
453 was passed to the consturctor.
454 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400455 if self._ManifestUrlHasSecureScheme():
456 return self._CheckForHookApprovalManifest()
457 else:
458 return self._CheckForHookApprovalHash()
459
460 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
461 changed_prompt):
462 """Check for approval for a particular attribute and hook.
463
464 Args:
465 subkey: The git config key under [repo.hooks.<hook_type>] to store the
466 last approved string.
467 new_val: The new value to compare against the last approved one.
468 main_prompt: Message to display to the user to ask for approval.
469 changed_prompt: Message explaining why we're re-asking for approval.
470
471 Returns:
472 True if this hook is approved to run; False otherwise.
473
474 Raises:
475 HookError: Raised if the user doesn't approve and abort_if_user_denies
476 was passed to the consturctor.
477 """
Doug Anderson37282b42011-03-04 11:54:18 -0800478 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800480
Mike Frysinger40252c22016-08-15 21:23:44 -0400481 # Get the last value that the user approved for this hook; may be None.
482 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800483
Mike Frysinger40252c22016-08-15 21:23:44 -0400484 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800485 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400486 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800487 # Approval matched. We're done.
488 return True
489 else:
490 # Give the user a reason why we're prompting, since they last told
491 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400492 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800493 else:
494 prompt = ''
495
496 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
497 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400498 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530499 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900500 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800501
502 # User is doing a one-time approval.
503 if response in ('y', 'yes'):
504 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400505 elif response == 'always':
506 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800507 return True
508
509 # For anything else, we'll assume no approval.
510 if self._abort_if_user_denies:
511 raise HookError('You must allow the %s hook or use --no-verify.' %
512 self._hook_type)
513
514 return False
515
Mike Frysinger40252c22016-08-15 21:23:44 -0400516 def _ManifestUrlHasSecureScheme(self):
517 """Check if the URI for the manifest is a secure transport."""
518 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
519 parse_results = urllib.parse.urlparse(self._manifest_url)
520 return parse_results.scheme in secure_schemes
521
522 def _CheckForHookApprovalManifest(self):
523 """Check whether the user has approved this manifest host.
524
525 Returns:
526 True if this hook is approved to run; False otherwise.
527 """
528 return self._CheckForHookApprovalHelper(
529 'approvedmanifest',
530 self._manifest_url,
531 'Run hook scripts from %s' % (self._manifest_url,),
532 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
533
534 def _CheckForHookApprovalHash(self):
535 """Check whether the user has approved the hooks repo.
536
537 Returns:
538 True if this hook is approved to run; False otherwise.
539 """
540 prompt = ('Repo %s run the script:\n'
541 ' %s\n'
542 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700543 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400544 return self._CheckForHookApprovalHelper(
545 'approvedhash',
546 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700547 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400548 'Scripts have changed since %s was allowed.' % (self._hook_type,))
549
Mike Frysingerf7c51602019-06-18 17:23:39 -0400550 @staticmethod
551 def _ExtractInterpFromShebang(data):
552 """Extract the interpreter used in the shebang.
553
554 Try to locate the interpreter the script is using (ignoring `env`).
555
556 Args:
557 data: The file content of the script.
558
559 Returns:
560 The basename of the main script interpreter, or None if a shebang is not
561 used or could not be parsed out.
562 """
563 firstline = data.splitlines()[:1]
564 if not firstline:
565 return None
566
567 # The format here can be tricky.
568 shebang = firstline[0].strip()
569 m = re.match(r'^#!\s*([^\s]+)(?:\s+([^\s]+))?', shebang)
570 if not m:
571 return None
572
573 # If the using `env`, find the target program.
574 interp = m.group(1)
575 if os.path.basename(interp) == 'env':
576 interp = m.group(2)
577
578 return interp
579
580 def _ExecuteHookViaReexec(self, interp, context, **kwargs):
581 """Execute the hook script through |interp|.
582
583 Note: Support for this feature should be dropped ~Jun 2021.
584
585 Args:
586 interp: The Python program to run.
587 context: Basic Python context to execute the hook inside.
588 kwargs: Arbitrary arguments to pass to the hook script.
589
590 Raises:
591 HookError: When the hooks failed for any reason.
592 """
593 # This logic needs to be kept in sync with _ExecuteHookViaImport below.
594 script = """
595import json, os, sys
596path = '''%(path)s'''
597kwargs = json.loads('''%(kwargs)s''')
598context = json.loads('''%(context)s''')
599sys.path.insert(0, os.path.dirname(path))
600data = open(path).read()
601exec(compile(data, path, 'exec'), context)
602context['main'](**kwargs)
603""" % {
604 'path': self._script_fullpath,
605 'kwargs': json.dumps(kwargs),
606 'context': json.dumps(context),
607 }
608
609 # We pass the script via stdin to avoid OS argv limits. It also makes
610 # unhandled exception tracebacks less verbose/confusing for users.
611 cmd = [interp, '-c', 'import sys; exec(sys.stdin.read())']
612 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
613 proc.communicate(input=script.encode('utf-8'))
614 if proc.returncode:
615 raise HookError('Failed to run %s hook.' % (self._hook_type,))
616
617 def _ExecuteHookViaImport(self, data, context, **kwargs):
618 """Execute the hook code in |data| directly.
619
620 Args:
621 data: The code of the hook to execute.
622 context: Basic Python context to execute the hook inside.
623 kwargs: Arbitrary arguments to pass to the hook script.
624
625 Raises:
626 HookError: When the hooks failed for any reason.
627 """
628 # Exec, storing global context in the context dict. We catch exceptions
629 # and convert to a HookError w/ just the failing traceback.
630 try:
631 exec(compile(data, self._script_fullpath, 'exec'), context)
632 except Exception:
633 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
634 (traceback.format_exc(), self._hook_type))
635
636 # Running the script should have defined a main() function.
637 if 'main' not in context:
638 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
639
640 # Call the main function in the hook. If the hook should cause the
641 # build to fail, it will raise an Exception. We'll catch that convert
642 # to a HookError w/ just the failing traceback.
643 try:
644 context['main'](**kwargs)
645 except Exception:
646 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
647 'above.' % (traceback.format_exc(), self._hook_type))
648
Doug Anderson37282b42011-03-04 11:54:18 -0800649 def _ExecuteHook(self, **kwargs):
650 """Actually execute the given hook.
651
652 This will run the hook's 'main' function in our python interpreter.
653
654 Args:
655 kwargs: Keyword arguments to pass to the hook. These are often specific
656 to the hook type. For instance, pre-upload hooks will contain
657 a project_list.
658 """
659 # Keep sys.path and CWD stashed away so that we can always restore them
660 # upon function exit.
661 orig_path = os.getcwd()
662 orig_syspath = sys.path
663
664 try:
665 # Always run hooks with CWD as topdir.
666 os.chdir(self._topdir)
667
668 # Put the hook dir as the first item of sys.path so hooks can do
669 # relative imports. We want to replace the repo dir as [0] so
670 # hooks can't import repo files.
671 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
672
Mike Frysingerf7c51602019-06-18 17:23:39 -0400673 # Initial global context for the hook to run within.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500674 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800675
Doug Anderson37282b42011-03-04 11:54:18 -0800676 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
677 # We don't actually want hooks to define their main with this argument--
678 # it's there to remind them that their hook should always take **kwargs.
679 # For instance, a pre-upload hook should be defined like:
680 # def main(project_list, **kwargs):
681 #
682 # This allows us to later expand the API without breaking old hooks.
683 kwargs = kwargs.copy()
684 kwargs['hook_should_take_kwargs'] = True
685
Mike Frysingerf7c51602019-06-18 17:23:39 -0400686 # See what version of python the hook has been written against.
687 data = open(self._script_fullpath).read()
688 interp = self._ExtractInterpFromShebang(data)
689 reexec = False
690 if interp:
691 prog = os.path.basename(interp)
692 if prog.startswith('python2') and sys.version_info.major != 2:
693 reexec = True
694 elif prog.startswith('python3') and sys.version_info.major == 2:
695 reexec = True
696
697 # Attempt to execute the hooks through the requested version of Python.
698 if reexec:
699 try:
700 self._ExecuteHookViaReexec(interp, context, **kwargs)
701 except OSError as e:
702 if e.errno == errno.ENOENT:
703 # We couldn't find the interpreter, so fallback to importing.
704 reexec = False
705 else:
706 raise
707
708 # Run the hook by importing directly.
709 if not reexec:
710 self._ExecuteHookViaImport(data, context, **kwargs)
Doug Anderson37282b42011-03-04 11:54:18 -0800711 finally:
712 # Restore sys.path and CWD.
713 sys.path = orig_syspath
714 os.chdir(orig_path)
715
716 def Run(self, user_allows_all_hooks, **kwargs):
717 """Run the hook.
718
719 If the hook doesn't exist (because there is no hooks project or because
720 this particular hook is not enabled), this is a no-op.
721
722 Args:
723 user_allows_all_hooks: If True, we will never prompt about running the
724 hook--we'll just assume it's OK to run it.
725 kwargs: Keyword arguments to pass to the hook. These are often specific
726 to the hook type. For instance, pre-upload hooks will contain
727 a project_list.
728
729 Raises:
730 HookError: If there was a problem finding the hook or the user declined
731 to run a required hook (from _CheckForHookApproval).
732 """
733 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700734 if ((not self._hooks_project) or (self._hook_type not in
735 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800736 return
737
738 # Bail with a nice error if we can't find the hook.
739 if not os.path.isfile(self._script_fullpath):
740 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
741
742 # Make sure the user is OK with running the hook.
743 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
744 return
745
746 # Run the hook with the same version of python we're using.
747 self._ExecuteHook(**kwargs)
748
749
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700750class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600751 # These objects can be shared between several working trees.
752 shareable_files = ['description', 'info']
753 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
754 # These objects can only be used by a single working tree.
755 working_tree_files = ['config', 'packed-refs', 'shallow']
756 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700757
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 def __init__(self,
759 manifest,
760 name,
761 remote,
762 gitdir,
David James8d201162013-10-11 17:03:19 -0700763 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700764 worktree,
765 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700766 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800767 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100768 rebase=True,
769 groups=None,
770 sync_c=False,
771 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900772 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100773 clone_depth=None,
774 upstream=None,
775 parent=None,
776 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900777 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700778 optimized_fetch=False,
779 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800780 """Init a Project object.
781
782 Args:
783 manifest: The XmlManifest object.
784 name: The `name` attribute of manifest.xml's project element.
785 remote: RemoteSpec object specifying its remote's properties.
786 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700787 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800788 worktree: Absolute path of git working tree.
789 relpath: Relative path of git working tree to repo's top directory.
790 revisionExpr: The `revision` attribute of manifest.xml's project element.
791 revisionId: git commit id for checking out.
792 rebase: The `rebase` attribute of manifest.xml's project element.
793 groups: The `groups` attribute of manifest.xml's project element.
794 sync_c: The `sync-c` attribute of manifest.xml's project element.
795 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900796 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800797 upstream: The `upstream` attribute of manifest.xml's project element.
798 parent: The parent Project object.
799 is_derived: False if the project was explicitly defined in the manifest;
800 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400801 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900802 optimized_fetch: If True, when a project is set to a sha1 revision, only
803 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700804 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800805 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700806 self.manifest = manifest
807 self.name = name
808 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800809 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700810 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800811 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700812 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800813 else:
814 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700815 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700816 self.revisionExpr = revisionExpr
817
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700818 if revisionId is None \
819 and revisionExpr \
820 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700821 self.revisionId = revisionExpr
822 else:
823 self.revisionId = revisionId
824
Mike Pontillod3153822012-02-28 11:53:24 -0800825 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700826 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700827 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800828 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900829 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900830 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700831 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800832 self.parent = parent
833 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900834 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800835 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800836
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700837 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700838 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500839 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500840 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700841 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
842 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800844 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700845 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800846 else:
847 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700848 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700849 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700850 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400851 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700852 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700853
Doug Anderson37282b42011-03-04 11:54:18 -0800854 # This will be filled in if a project is later identified to be the
855 # project containing repo hooks.
856 self.enabled_repo_hooks = []
857
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800859 def Derived(self):
860 return self.is_derived
861
862 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700864 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700865
866 @property
867 def CurrentBranch(self):
868 """Obtain the name of the currently checked out branch.
869 The branch name omits the 'refs/heads/' prefix.
870 None is returned if the project is on a detached HEAD.
871 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700872 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 if b.startswith(R_HEADS):
874 return b[len(R_HEADS):]
875 return None
876
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700877 def IsRebaseInProgress(self):
878 w = self.worktree
879 g = os.path.join(w, '.git')
880 return os.path.exists(os.path.join(g, 'rebase-apply')) \
881 or os.path.exists(os.path.join(g, 'rebase-merge')) \
882 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200883
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884 def IsDirty(self, consider_untracked=True):
885 """Is the working directory modified in some way?
886 """
887 self.work_git.update_index('-q',
888 '--unmerged',
889 '--ignore-missing',
890 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900891 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892 return True
893 if self.work_git.DiffZ('diff-files'):
894 return True
895 if consider_untracked and self.work_git.LsOthers():
896 return True
897 return False
898
899 _userident_name = None
900 _userident_email = None
901
902 @property
903 def UserName(self):
904 """Obtain the user's personal name.
905 """
906 if self._userident_name is None:
907 self._LoadUserIdentity()
908 return self._userident_name
909
910 @property
911 def UserEmail(self):
912 """Obtain the user's email address. This is very likely
913 to be their Gerrit login.
914 """
915 if self._userident_email is None:
916 self._LoadUserIdentity()
917 return self._userident_email
918
919 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900920 u = self.bare_git.var('GIT_COMMITTER_IDENT')
921 m = re.compile("^(.*) <([^>]*)> ").match(u)
922 if m:
923 self._userident_name = m.group(1)
924 self._userident_email = m.group(2)
925 else:
926 self._userident_name = ''
927 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928
929 def GetRemote(self, name):
930 """Get the configuration for a single remote.
931 """
932 return self.config.GetRemote(name)
933
934 def GetBranch(self, name):
935 """Get the configuration for a single branch.
936 """
937 return self.config.GetBranch(name)
938
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700939 def GetBranches(self):
940 """Get all existing local branches.
941 """
942 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900943 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700944 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700945
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530946 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700947 if name.startswith(R_HEADS):
948 name = name[len(R_HEADS):]
949 b = self.GetBranch(name)
950 b.current = name == current
951 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900952 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700953 heads[name] = b
954
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530955 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700956 if name.startswith(R_PUB):
957 name = name[len(R_PUB):]
958 b = heads.get(name)
959 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900960 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700961
962 return heads
963
Colin Cross5acde752012-03-28 20:15:45 -0700964 def MatchesGroups(self, manifest_groups):
965 """Returns true if the manifest groups specified at init should cause
966 this project to be synced.
967 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700968 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700969
970 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700971 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700972 manifest_groups: "-group1,group2"
973 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500974
975 The special manifest group "default" will match any project that
976 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700977 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500978 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700979 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700980 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500981 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700982
Conley Owens971de8e2012-04-16 10:36:08 -0700983 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700984 for group in expanded_manifest_groups:
985 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700986 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700987 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700988 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700989
Conley Owens971de8e2012-04-16 10:36:08 -0700990 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700991
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700992# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700993 def UncommitedFiles(self, get_all=True):
994 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700995
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700996 Args:
997 get_all: a boolean, if True - get information about all different
998 uncommitted files. If False - return as soon as any kind of
999 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001000 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001001 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001002 self.work_git.update_index('-q',
1003 '--unmerged',
1004 '--ignore-missing',
1005 '--refresh')
1006 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001007 details.append("rebase in progress")
1008 if not get_all:
1009 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001010
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001011 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
1012 if changes:
1013 details.extend(changes)
1014 if not get_all:
1015 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001016
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001017 changes = self.work_git.DiffZ('diff-files').keys()
1018 if changes:
1019 details.extend(changes)
1020 if not get_all:
1021 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001022
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001023 changes = self.work_git.LsOthers()
1024 if changes:
1025 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001026
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001027 return details
1028
1029 def HasChanges(self):
1030 """Returns true if there are uncommitted changes.
1031 """
1032 if self.UncommitedFiles(get_all=False):
1033 return True
1034 else:
1035 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001036
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001037 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +02001039
1040 Args:
1041 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001042 quiet: If True then only print the project name. Do not print
1043 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044 """
Renaud Paquaybed8b622018-09-27 10:46:58 -07001045 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001046 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +02001047 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -07001048 print(file=output_redir)
1049 print('project %s/' % self.relpath, file=output_redir)
1050 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001051 return
1052
1053 self.work_git.update_index('-q',
1054 '--unmerged',
1055 '--ignore-missing',
1056 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001057 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001058 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
1059 df = self.work_git.DiffZ('diff-files')
1060 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +01001061 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001062 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063
1064 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001065 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +02001066 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -07001067 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001069 if quiet:
1070 out.nl()
1071 return 'DIRTY'
1072
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001073 branch = self.CurrentBranch
1074 if branch is None:
1075 out.nobranch('(*** NO BRANCH ***)')
1076 else:
1077 out.branch('branch %s', branch)
1078 out.nl()
1079
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001080 if rb:
1081 out.important('prior sync failed; rebase still in progress')
1082 out.nl()
1083
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 paths = list()
1085 paths.extend(di.keys())
1086 paths.extend(df.keys())
1087 paths.extend(do)
1088
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001090 try:
1091 i = di[p]
1092 except KeyError:
1093 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001095 try:
1096 f = df[p]
1097 except KeyError:
1098 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +02001099
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001100 if i:
1101 i_status = i.status.upper()
1102 else:
1103 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001104
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001105 if f:
1106 f_status = f.status.lower()
1107 else:
1108 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001109
1110 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001111 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001112 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001113 else:
1114 line = ' %s%s\t%s' % (i_status, f_status, p)
1115
1116 if i and not f:
1117 out.added('%s', line)
1118 elif (i and f) or (not i and f):
1119 out.changed('%s', line)
1120 elif not i and not f:
1121 out.untracked('%s', line)
1122 else:
1123 out.write('%s', line)
1124 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001125
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001126 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001127
pelyad67872d2012-03-28 14:49:58 +03001128 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001129 """Prints the status of the repository to stdout.
1130 """
1131 out = DiffColoring(self.config)
1132 cmd = ['diff']
1133 if out.is_on:
1134 cmd.append('--color')
1135 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001136 if absolute_paths:
1137 cmd.append('--src-prefix=a/%s/' % self.relpath)
1138 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001139 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -04001140 try:
1141 p = GitCommand(self,
1142 cmd,
1143 capture_stdout=True,
1144 capture_stderr=True)
1145 except GitError as e:
1146 out.nl()
1147 out.project('project %s/' % self.relpath)
1148 out.nl()
1149 out.fail('%s', str(e))
1150 out.nl()
1151 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001152 has_diff = False
1153 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -04001154 if not hasattr(line, 'encode'):
1155 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001156 if not has_diff:
1157 out.nl()
1158 out.project('project %s/' % self.relpath)
1159 out.nl()
1160 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001161 print(line[:-1])
Mike Frysinger0a9265e2019-09-30 23:59:27 -04001162 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001163
1164
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001165# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001166
David Pursehouse8a68ff92012-09-24 12:15:13 +09001167 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001168 """Was the branch published (uploaded) for code review?
1169 If so, returns the SHA-1 hash of the last published
1170 state for the branch.
1171 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001172 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001173 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001174 try:
1175 return self.bare_git.rev_parse(key)
1176 except GitError:
1177 return None
1178 else:
1179 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001180 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001181 except KeyError:
1182 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001183
David Pursehouse8a68ff92012-09-24 12:15:13 +09001184 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001185 """Prunes any stale published refs.
1186 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001187 if all_refs is None:
1188 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001189 heads = set()
1190 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301191 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192 if name.startswith(R_HEADS):
1193 heads.add(name)
1194 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001195 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001196
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301197 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001198 n = name[len(R_PUB):]
1199 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001200 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001202 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203 """List any branches which can be uploaded for review.
1204 """
1205 heads = {}
1206 pubed = {}
1207
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301208 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001209 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001210 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001211 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001212 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001213
1214 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301215 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001216 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001217 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001218 if selected_branch and branch != selected_branch:
1219 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001220
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001221 rb = self.GetUploadableBranch(branch)
1222 if rb:
1223 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001224 return ready
1225
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001226 def GetUploadableBranch(self, branch_name):
1227 """Get a single uploadable branch, or None.
1228 """
1229 branch = self.GetBranch(branch_name)
1230 base = branch.LocalMerge
1231 if branch.LocalMerge:
1232 rb = ReviewableBranch(self, branch, base)
1233 if rb.commits:
1234 return rb
1235 return None
1236
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001237 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001238 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001239 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001240 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001241 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001242 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001243 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001244 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001245 validate_certs=True,
1246 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001247 """Uploads the named branch for code review.
1248 """
1249 if branch is None:
1250 branch = self.CurrentBranch
1251 if branch is None:
1252 raise GitError('not currently on a branch')
1253
1254 branch = self.GetBranch(branch)
1255 if not branch.LocalMerge:
1256 raise GitError('branch %s does not track a remote' % branch.name)
1257 if not branch.remote.review:
1258 raise GitError('remote %s has no review url' % branch.remote.name)
1259
Bryan Jacobsf609f912013-05-06 13:36:24 -04001260 if dest_branch is None:
1261 dest_branch = self.dest_branch
1262 if dest_branch is None:
1263 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001264 if not dest_branch.startswith(R_HEADS):
1265 dest_branch = R_HEADS + dest_branch
1266
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001267 if not branch.remote.projectname:
1268 branch.remote.projectname = self.name
1269 branch.remote.Save()
1270
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001271 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001272 if url is None:
1273 raise UploadError('review not configured')
1274 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001275
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001276 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001277 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001278
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001279 for push_option in (push_options or []):
1280 cmd.append('-o')
1281 cmd.append(push_option)
1282
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001283 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001284
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001285 if dest_branch.startswith(R_HEADS):
1286 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001287
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001288 upload_type = 'for'
1289 if draft:
1290 upload_type = 'drafts'
1291
1292 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1293 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001294 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001295 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001296 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001297
David Pursehousef25a3702018-11-14 19:01:22 -08001298 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001299 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001300 if notify:
1301 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001302 if private:
1303 opts += ['private']
1304 if wip:
1305 opts += ['wip']
1306 if opts:
1307 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001308 cmd.append(ref_spec)
1309
Anthony King7bdac712014-07-16 12:56:40 +01001310 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001311 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001312
1313 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1314 self.bare_git.UpdateRef(R_PUB + branch.name,
1315 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001316 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001317
1318
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001319# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001320
Julien Campergue335f5ef2013-10-16 11:02:35 +02001321 def _ExtractArchive(self, tarpath, path=None):
1322 """Extract the given tar on its current location
1323
1324 Args:
1325 - tarpath: The path to the actual tar file
1326
1327 """
1328 try:
1329 with tarfile.open(tarpath, 'r') as tar:
1330 tar.extractall(path=path)
1331 return True
1332 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001333 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001334 return False
1335
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001336 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001337 quiet=False,
1338 is_new=None,
1339 current_branch_only=False,
1340 force_sync=False,
1341 clone_bundle=True,
1342 no_tags=False,
1343 archive=False,
1344 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001345 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001346 submodules=False,
1347 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001348 """Perform only the network IO portion of the sync process.
1349 Local working directory/branch state is not affected.
1350 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001351 if archive and not isinstance(self, MetaProject):
1352 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001353 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001354 return False
1355
1356 name = self.relpath.replace('\\', '/')
1357 name = name.replace('/', '_')
1358 tarpath = '%s.tar' % name
1359 topdir = self.manifest.topdir
1360
1361 try:
1362 self._FetchArchive(tarpath, cwd=topdir)
1363 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001364 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001365 return False
1366
1367 # From now on, we only need absolute tarpath
1368 tarpath = os.path.join(topdir, tarpath)
1369
1370 if not self._ExtractArchive(tarpath, path=topdir):
1371 return False
1372 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001373 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001374 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001375 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001376 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001377 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001378 if is_new is None:
1379 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001380 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001381 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001382 else:
1383 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001384 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001385
1386 if is_new:
1387 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1388 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001389 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001390 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001391 # This works for both absolute and relative alternate directories.
1392 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001393 finally:
1394 fd.close()
1395 except IOError:
1396 alt_dir = None
1397 else:
1398 alt_dir = None
1399
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001400 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001401 and alt_dir is None \
1402 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001403 is_new = False
1404
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001405 if not current_branch_only:
1406 if self.sync_c:
1407 current_branch_only = True
1408 elif not self.manifest._loaded:
1409 # Manifest cannot check defaults until it syncs.
1410 current_branch_only = False
1411 elif self.manifest.default.sync_c:
1412 current_branch_only = True
1413
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001414 if not no_tags:
1415 if not self.sync_tags:
1416 no_tags = True
1417
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001418 if self.clone_depth:
1419 depth = self.clone_depth
1420 else:
1421 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1422
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001423 need_to_fetch = not (optimized_fetch and
1424 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001425 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001426 if (need_to_fetch and
1427 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1428 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001429 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001430 submodules=submodules, force_sync=force_sync,
1431 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001432 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001433
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001434 mp = self.manifest.manifestProject
1435 dissociate = mp.config.GetBoolean('repo.dissociate')
1436 if dissociate:
1437 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1438 if os.path.exists(alternates_file):
1439 cmd = ['repack', '-a', '-d']
1440 if GitCommand(self, cmd, bare=True).Wait() != 0:
1441 return False
1442 platform_utils.remove(alternates_file)
1443
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001444 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001445 self._InitMRef()
1446 else:
1447 self._InitMirrorHead()
1448 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001449 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001450 except OSError:
1451 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001452 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001453
1454 def PostRepoUpgrade(self):
1455 self._InitHooks()
1456
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001457 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001458 if self.manifest.isGitcClient:
1459 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001460 for copyfile in self.copyfiles:
1461 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001462 for linkfile in self.linkfiles:
1463 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001464
Julien Camperguedd654222014-01-09 16:21:37 +01001465 def GetCommitRevisionId(self):
1466 """Get revisionId of a commit.
1467
1468 Use this method instead of GetRevisionId to get the id of the commit rather
1469 than the id of the current git object (for example, a tag)
1470
1471 """
1472 if not self.revisionExpr.startswith(R_TAGS):
1473 return self.GetRevisionId(self._allrefs)
1474
1475 try:
1476 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1477 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001478 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1479 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001480
David Pursehouse8a68ff92012-09-24 12:15:13 +09001481 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001482 if self.revisionId:
1483 return self.revisionId
1484
1485 rem = self.GetRemote(self.remote.name)
1486 rev = rem.ToLocal(self.revisionExpr)
1487
David Pursehouse8a68ff92012-09-24 12:15:13 +09001488 if all_refs is not None and rev in all_refs:
1489 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001490
1491 try:
1492 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1493 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001494 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1495 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001496
Martin Kellye4e94d22017-03-21 16:05:12 -07001497 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001498 """Perform only the local IO portion of the sync process.
1499 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001500 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001501 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001502 all_refs = self.bare_ref.all
1503 self.CleanPublishedCache(all_refs)
1504 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001505
David Pursehouse1d947b32012-10-25 12:23:11 +09001506 def _doff():
1507 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001508 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001509
Martin Kellye4e94d22017-03-21 16:05:12 -07001510 def _dosubmodules():
1511 self._SyncSubmodules(quiet=True)
1512
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001513 head = self.work_git.GetHead()
1514 if head.startswith(R_HEADS):
1515 branch = head[len(R_HEADS):]
1516 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001517 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001518 except KeyError:
1519 head = None
1520 else:
1521 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001522
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001523 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524 # Currently on a detached HEAD. The user is assumed to
1525 # not have any local modifications worth worrying about.
1526 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001527 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001528 syncbuf.fail(self, _PriorSyncFailedError())
1529 return
1530
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001531 if head == revid:
1532 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001533 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001534 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001535 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001536 # The copy/linkfile config may have changed.
1537 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001538 return
1539 else:
1540 lost = self._revlist(not_rev(revid), HEAD)
1541 if lost:
1542 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001543
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001545 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001546 if submodules:
1547 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001548 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001549 syncbuf.fail(self, e)
1550 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001551 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001552 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001554 if head == revid:
1555 # No changes; don't do anything further.
1556 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001557 # The copy/linkfile config may have changed.
1558 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001559 return
1560
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001561 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001563 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001564 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001565 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001566 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001567 syncbuf.info(self,
1568 "leaving %s; does not track upstream",
1569 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001570 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001571 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001572 if submodules:
1573 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001574 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001575 syncbuf.fail(self, e)
1576 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001577 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001578 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001579
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001580 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001581 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001583 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001584 if not_merged:
1585 if upstream_gain:
1586 # The user has published this branch and some of those
1587 # commits are not yet merged upstream. We do not want
1588 # to rewrite the published commits so we punt.
1589 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001590 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001591 "branch %s is published (but not merged) and is now "
1592 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001593 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001594 elif pub == head:
1595 # All published commits are merged, and thus we are a
1596 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001597 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001598 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001599 if submodules:
1600 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001601 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001602
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001603 # Examine the local commits not in the remote. Find the
1604 # last one attributed to this user, if any.
1605 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001606 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001607 last_mine = None
1608 cnt_mine = 0
1609 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001610 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001611 if committer_email == self.UserEmail:
1612 last_mine = commit_id
1613 cnt_mine += 1
1614
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001615 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001616 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001617
1618 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001619 syncbuf.fail(self, _DirtyError())
1620 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001621
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001622 # If the upstream switched on us, warn the user.
1623 #
1624 if branch.merge != self.revisionExpr:
1625 if branch.merge and self.revisionExpr:
1626 syncbuf.info(self,
1627 'manifest switched %s...%s',
1628 branch.merge,
1629 self.revisionExpr)
1630 elif branch.merge:
1631 syncbuf.info(self,
1632 'manifest no longer tracks %s',
1633 branch.merge)
1634
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001635 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001636 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001637 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001639 syncbuf.info(self,
1640 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001641 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001642
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001643 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001644 if not ID_RE.match(self.revisionExpr):
1645 # in case of manifest sync the revisionExpr might be a SHA1
1646 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001647 if not branch.merge.startswith('refs/'):
1648 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001649 branch.Save()
1650
Mike Pontillod3153822012-02-28 11:53:24 -08001651 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001652 def _docopyandlink():
1653 self._CopyAndLinkFiles()
1654
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001655 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001656 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001657 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001658 if submodules:
1659 syncbuf.later2(self, _dosubmodules)
1660 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001661 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001662 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001663 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001664 if submodules:
1665 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001666 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001667 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001668 syncbuf.fail(self, e)
1669 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001670 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001671 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001672 if submodules:
1673 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001674
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001675 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001676 # dest should already be an absolute path, but src is project relative
1677 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001678 abssrc = os.path.join(self.worktree, src)
1679 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001680
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001681 def AddLinkFile(self, src, dest, absdest):
1682 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001683 # make src relative path to dest
1684 absdestdir = os.path.dirname(absdest)
1685 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001686 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001687
James W. Mills24c13082012-04-12 15:04:13 -05001688 def AddAnnotation(self, name, value, keep):
1689 self.annotations.append(_Annotation(name, value, keep))
1690
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001691 def DownloadPatchSet(self, change_id, patch_id):
1692 """Download a single patch set of a single change to FETCH_HEAD.
1693 """
1694 remote = self.GetRemote(self.remote.name)
1695
1696 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001697 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001698 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001699 if GitCommand(self, cmd, bare=True).Wait() != 0:
1700 return None
1701 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001702 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001703 change_id,
1704 patch_id,
1705 self.bare_git.rev_parse('FETCH_HEAD'))
1706
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001707
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001708# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001709
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001710 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001711 """Create a new branch off the manifest's revision.
1712 """
Simran Basib9a1b732015-08-20 12:19:28 -07001713 if not branch_merge:
1714 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001715 head = self.work_git.GetHead()
1716 if head == (R_HEADS + name):
1717 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001718
David Pursehouse8a68ff92012-09-24 12:15:13 +09001719 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001720 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001721 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001722 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001723 capture_stdout=True,
1724 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001725
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001726 branch = self.GetBranch(name)
1727 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001728 branch.merge = branch_merge
1729 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1730 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001731
1732 if revision is None:
1733 revid = self.GetRevisionId(all_refs)
1734 else:
1735 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001736
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001737 if head.startswith(R_HEADS):
1738 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001739 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001740 except KeyError:
1741 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001742 if revid and head and revid == head:
1743 ref = os.path.join(self.gitdir, R_HEADS + name)
1744 try:
1745 os.makedirs(os.path.dirname(ref))
1746 except OSError:
1747 pass
1748 _lwrite(ref, '%s\n' % revid)
1749 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1750 'ref: %s%s\n' % (R_HEADS, name))
1751 branch.Save()
1752 return True
1753
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001754 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001755 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001756 capture_stdout=True,
1757 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001758 branch.Save()
1759 return True
1760 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001761
Wink Saville02d79452009-04-10 13:01:24 -07001762 def CheckoutBranch(self, name):
1763 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001764
1765 Args:
1766 name: The name of the branch to checkout.
1767
1768 Returns:
1769 True if the checkout succeeded; False if it didn't; None if the branch
1770 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001771 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001772 rev = R_HEADS + name
1773 head = self.work_git.GetHead()
1774 if head == rev:
1775 # Already on the branch
1776 #
1777 return True
Wink Saville02d79452009-04-10 13:01:24 -07001778
David Pursehouse8a68ff92012-09-24 12:15:13 +09001779 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001780 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001781 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001782 except KeyError:
1783 # Branch does not exist in this project
1784 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001785 return None
Wink Saville02d79452009-04-10 13:01:24 -07001786
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001787 if head.startswith(R_HEADS):
1788 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001789 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001790 except KeyError:
1791 head = None
1792
1793 if head == revid:
1794 # Same revision; just update HEAD to point to the new
1795 # target branch, but otherwise take no other action.
1796 #
1797 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1798 'ref: %s%s\n' % (R_HEADS, name))
1799 return True
1800
1801 return GitCommand(self,
1802 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001803 capture_stdout=True,
1804 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001805
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001806 def AbandonBranch(self, name):
1807 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001808
1809 Args:
1810 name: The name of the branch to abandon.
1811
1812 Returns:
1813 True if the abandon succeeded; False if it didn't; None if the branch
1814 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001815 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001816 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001817 all_refs = self.bare_ref.all
1818 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001819 # Doesn't exist
1820 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001821
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001822 head = self.work_git.GetHead()
1823 if head == rev:
1824 # We can't destroy the branch while we are sitting
1825 # on it. Switch to a detached HEAD.
1826 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001827 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001828
David Pursehouse8a68ff92012-09-24 12:15:13 +09001829 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001830 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001831 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1832 '%s\n' % revid)
1833 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001834 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001835
1836 return GitCommand(self,
1837 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001838 capture_stdout=True,
1839 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001840
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001841 def PruneHeads(self):
1842 """Prune any topic branches already merged into upstream.
1843 """
1844 cb = self.CurrentBranch
1845 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001846 left = self._allrefs
1847 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001848 if name.startswith(R_HEADS):
1849 name = name[len(R_HEADS):]
1850 if cb is None or name != cb:
1851 kill.append(name)
1852
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001853 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001854 if cb is not None \
1855 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001856 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001857 self.work_git.DetachHead(HEAD)
1858 kill.append(cb)
1859
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001860 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001861 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001862
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001863 try:
1864 self.bare_git.DetachHead(rev)
1865
1866 b = ['branch', '-d']
1867 b.extend(kill)
1868 b = GitCommand(self, b, bare=True,
1869 capture_stdout=True,
1870 capture_stderr=True)
1871 b.Wait()
1872 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001873 if ID_RE.match(old):
1874 self.bare_git.DetachHead(old)
1875 else:
1876 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001877 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001878
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001879 for branch in kill:
1880 if (R_HEADS + branch) not in left:
1881 self.CleanPublishedCache()
1882 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001883
1884 if cb and cb not in kill:
1885 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001886 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001887
1888 kept = []
1889 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001890 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001891 branch = self.GetBranch(branch)
1892 base = branch.LocalMerge
1893 if not base:
1894 base = rev
1895 kept.append(ReviewableBranch(self, branch, base))
1896 return kept
1897
1898
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001899# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001900
1901 def GetRegisteredSubprojects(self):
1902 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001903
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001904 def rec(subprojects):
1905 if not subprojects:
1906 return
1907 result.extend(subprojects)
1908 for p in subprojects:
1909 rec(p.subprojects)
1910 rec(self.subprojects)
1911 return result
1912
1913 def _GetSubmodules(self):
1914 # Unfortunately we cannot call `git submodule status --recursive` here
1915 # because the working tree might not exist yet, and it cannot be used
1916 # without a working tree in its current implementation.
1917
1918 def get_submodules(gitdir, rev):
1919 # Parse .gitmodules for submodule sub_paths and sub_urls
1920 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1921 if not sub_paths:
1922 return []
1923 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1924 # revision of submodule repository
1925 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1926 submodules = []
1927 for sub_path, sub_url in zip(sub_paths, sub_urls):
1928 try:
1929 sub_rev = sub_revs[sub_path]
1930 except KeyError:
1931 # Ignore non-exist submodules
1932 continue
1933 submodules.append((sub_rev, sub_path, sub_url))
1934 return submodules
1935
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001936 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1937 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001938
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001939 def parse_gitmodules(gitdir, rev):
1940 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1941 try:
Anthony King7bdac712014-07-16 12:56:40 +01001942 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1943 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001944 except GitError:
1945 return [], []
1946 if p.Wait() != 0:
1947 return [], []
1948
1949 gitmodules_lines = []
1950 fd, temp_gitmodules_path = tempfile.mkstemp()
1951 try:
1952 os.write(fd, p.stdout)
1953 os.close(fd)
1954 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001955 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1956 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001957 if p.Wait() != 0:
1958 return [], []
1959 gitmodules_lines = p.stdout.split('\n')
1960 except GitError:
1961 return [], []
1962 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001963 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001964
1965 names = set()
1966 paths = {}
1967 urls = {}
1968 for line in gitmodules_lines:
1969 if not line:
1970 continue
1971 m = re_path.match(line)
1972 if m:
1973 names.add(m.group(1))
1974 paths[m.group(1)] = m.group(2)
1975 continue
1976 m = re_url.match(line)
1977 if m:
1978 names.add(m.group(1))
1979 urls[m.group(1)] = m.group(2)
1980 continue
1981 names = sorted(names)
1982 return ([paths.get(name, '') for name in names],
1983 [urls.get(name, '') for name in names])
1984
1985 def git_ls_tree(gitdir, rev, paths):
1986 cmd = ['ls-tree', rev, '--']
1987 cmd.extend(paths)
1988 try:
Anthony King7bdac712014-07-16 12:56:40 +01001989 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1990 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001991 except GitError:
1992 return []
1993 if p.Wait() != 0:
1994 return []
1995 objects = {}
1996 for line in p.stdout.split('\n'):
1997 if not line.strip():
1998 continue
1999 object_rev, object_path = line.split()[2:4]
2000 objects[object_path] = object_rev
2001 return objects
2002
2003 try:
2004 rev = self.GetRevisionId()
2005 except GitError:
2006 return []
2007 return get_submodules(self.gitdir, rev)
2008
2009 def GetDerivedSubprojects(self):
2010 result = []
2011 if not self.Exists:
2012 # If git repo does not exist yet, querying its submodules will
2013 # mess up its states; so return here.
2014 return result
2015 for rev, path, url in self._GetSubmodules():
2016 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07002017 relpath, worktree, gitdir, objdir = \
2018 self.manifest.GetSubprojectPaths(self, name, path)
2019 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002020 if project:
2021 result.extend(project.GetDerivedSubprojects())
2022 continue
David James8d201162013-10-11 17:03:19 -07002023
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08002024 if url.startswith('..'):
2025 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002026 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01002027 url=url,
Steve Raed6480452016-08-10 15:00:00 -07002028 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01002029 review=self.remote.review,
2030 revision=self.remote.revision)
2031 subproject = Project(manifest=self.manifest,
2032 name=name,
2033 remote=remote,
2034 gitdir=gitdir,
2035 objdir=objdir,
2036 worktree=worktree,
2037 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02002038 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01002039 revisionId=rev,
2040 rebase=self.rebase,
2041 groups=self.groups,
2042 sync_c=self.sync_c,
2043 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09002044 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01002045 parent=self,
2046 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002047 result.append(subproject)
2048 result.extend(subproject.GetDerivedSubprojects())
2049 return result
2050
2051
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002052# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002053 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002054 try:
2055 # if revision (sha or tag) is not present then following function
2056 # throws an error.
2057 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2058 return True
2059 except GitError:
2060 # There is no such persistent revision. We have to fetch it.
2061 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002062
Julien Campergue335f5ef2013-10-16 11:02:35 +02002063 def _FetchArchive(self, tarpath, cwd=None):
2064 cmd = ['archive', '-v', '-o', tarpath]
2065 cmd.append('--remote=%s' % self.remote.url)
2066 cmd.append('--prefix=%s/' % self.relpath)
2067 cmd.append(self.revisionExpr)
2068
2069 command = GitCommand(self, cmd, cwd=cwd,
2070 capture_stdout=True,
2071 capture_stderr=True)
2072
2073 if command.Wait() != 0:
2074 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2075
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002076 def _RemoteFetch(self, name=None,
2077 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002078 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002079 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002080 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002081 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002082 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002083 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002084 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07002085 force_sync=False,
2086 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087
2088 is_sha1 = False
2089 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002090 # The depth should not be used when fetching to a mirror because
2091 # it will result in a shallow repository that cannot be cloned or
2092 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002093 # The repo project should also never be synced with partial depth.
2094 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2095 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002096
Shawn Pearce69e04d82014-01-29 12:48:54 -08002097 if depth:
2098 current_branch_only = True
2099
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002100 if ID_RE.match(self.revisionExpr) is not None:
2101 is_sha1 = True
2102
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002103 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002104 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002105 # this is a tag and its sha1 value should never change
2106 tag_name = self.revisionExpr[len(R_TAGS):]
2107
2108 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002109 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002110 if not quiet:
2111 print('Skipped fetching project %s (already have persistent ref)'
2112 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002113 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002114 if is_sha1 and not depth:
2115 # When syncing a specific commit and --depth is not set:
2116 # * if upstream is explicitly specified and is not a sha1, fetch only
2117 # upstream as users expect only upstream to be fetch.
2118 # Note: The commit might not be in upstream in which case the sync
2119 # will fail.
2120 # * otherwise, fetch all branches to make sure we end up with the
2121 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002122 if self.upstream:
2123 current_branch_only = not ID_RE.match(self.upstream)
2124 else:
2125 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002126
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002127 if not name:
2128 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002129
2130 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002131 remote = self.GetRemote(name)
2132 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002133 ssh_proxy = True
2134
Shawn O. Pearce88443382010-10-08 10:02:09 +02002135 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002136 if alt_dir and 'objects' == os.path.basename(alt_dir):
2137 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002138 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2139 remote = self.GetRemote(name)
2140
David Pursehouse8a68ff92012-09-24 12:15:13 +09002141 all_refs = self.bare_ref.all
2142 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002143 tmp = set()
2144
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302145 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002146 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002147 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002148 all_refs[r] = ref_id
2149 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002150 continue
2151
David Pursehouse8a68ff92012-09-24 12:15:13 +09002152 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002153 continue
2154
David Pursehouse8a68ff92012-09-24 12:15:13 +09002155 r = 'refs/_alt/%s' % ref_id
2156 all_refs[r] = ref_id
2157 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002158 tmp.add(r)
2159
heping3d7bbc92017-04-12 19:51:47 +08002160 tmp_packed_lines = []
2161 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002162
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302163 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002164 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002165 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002166 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002167 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002168
heping3d7bbc92017-04-12 19:51:47 +08002169 tmp_packed = ''.join(tmp_packed_lines)
2170 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002171 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002172 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002173 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002174
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002175 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002176
Xin Li745be2e2019-06-03 11:24:30 -07002177 if clone_filter:
2178 git_require((2, 19, 0), fail=True, msg='partial clones')
2179 cmd.append('--filter=%s' % clone_filter)
2180 self.config.SetString('extensions.partialclone', self.remote.name)
2181
Conley Owensf97e8382015-01-21 11:12:46 -08002182 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002183 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002184 else:
2185 # If this repo has shallow objects, then we don't know which refs have
2186 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2187 # do this with projects that don't have shallow objects, since it is less
2188 # efficient.
2189 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2190 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002191
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002192 if quiet:
2193 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002194 if not self.worktree:
2195 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002196 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002197
Xin Lia2cd6ae2019-09-16 10:55:41 -07002198 spec = []
2199
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002200 # If using depth then we should not get all the tags since they may
2201 # be outside of the depth.
2202 if no_tags or depth:
2203 cmd.append('--no-tags')
2204 else:
2205 cmd.append('--tags')
Xin Lia2cd6ae2019-09-16 10:55:41 -07002206 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002207
Mike Frysingere57f1142019-03-18 21:27:54 -04002208 if force_sync:
2209 cmd.append('--force')
2210
David Pursehouse74cfd272015-10-14 10:50:15 +09002211 if prune:
2212 cmd.append('--prune')
2213
Martin Kellye4e94d22017-03-21 16:05:12 -07002214 if submodules:
2215 cmd.append('--recurse-submodules=on-demand')
2216
Brian Harring14a66742012-09-28 20:21:57 -07002217 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002218 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002219 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002220 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002221 spec.append('tag')
2222 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002223
David Pursehouse403b64e2015-04-27 10:41:33 +09002224 if not self.manifest.IsMirror:
2225 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002226 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002227 # Shallow checkout of a specific commit, fetch from that commit and not
2228 # the heads only as the commit might be deeper in the history.
2229 spec.append(branch)
2230 else:
2231 if is_sha1:
2232 branch = self.upstream
2233 if branch is not None and branch.strip():
2234 if not branch.startswith('refs/'):
2235 branch = R_HEADS + branch
2236 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002237 cmd.extend(spec)
2238
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002239 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002240 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002241 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002242 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002243 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002244 ok = True
2245 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002246 # If needed, run the 'git remote prune' the first time through the loop
2247 elif (not _i and
2248 "error:" in gitcmd.stderr and
2249 "git remote prune" in gitcmd.stderr):
2250 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002251 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002252 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002253 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002254 break
2255 continue
Brian Harring14a66742012-09-28 20:21:57 -07002256 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002257 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2258 # in sha1 mode, we just tried sync'ing from the upstream field; it
2259 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002260 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002261 elif ret < 0:
2262 # Git died with a signal, exit immediately
2263 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002264 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002265
2266 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002267 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002268 if old_packed != '':
2269 _lwrite(packed_refs, old_packed)
2270 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002271 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002272 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002273
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002274 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002275 # We just synced the upstream given branch; verify we
2276 # got what we wanted, else trigger a second run of all
2277 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002278 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002279 if current_branch_only and depth:
2280 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002281 return self._RemoteFetch(name=name,
2282 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002283 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002284 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002285 else:
2286 # Avoid infinite recursion: sync all branches with depth set to None
2287 return self._RemoteFetch(name=name, current_branch_only=False,
2288 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002289 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002290
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002291 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002292
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002293 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002294 if initial and \
2295 (self.manifest.manifestProject.config.GetString('repo.depth') or
2296 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002297 return False
2298
2299 remote = self.GetRemote(self.remote.name)
2300 bundle_url = remote.url + '/clone.bundle'
2301 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002302 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2303 'persistent-http',
2304 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002305 return False
2306
2307 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2308 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2309
2310 exist_dst = os.path.exists(bundle_dst)
2311 exist_tmp = os.path.exists(bundle_tmp)
2312
2313 if not initial and not exist_dst and not exist_tmp:
2314 return False
2315
2316 if not exist_dst:
2317 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2318 if not exist_dst:
2319 return False
2320
2321 cmd = ['fetch']
2322 if quiet:
2323 cmd.append('--quiet')
2324 if not self.worktree:
2325 cmd.append('--update-head-ok')
2326 cmd.append(bundle_dst)
2327 for f in remote.fetch:
2328 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002329 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002330
2331 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002332 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002333 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002334 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002335 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002336 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002337
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002338 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002339 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002340 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002341
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002342 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002343 if quiet:
2344 cmd += ['--silent']
2345 if os.path.exists(tmpPath):
2346 size = os.stat(tmpPath).st_size
2347 if size >= 1024:
2348 cmd += ['--continue-at', '%d' % (size,)]
2349 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002350 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002351 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002352 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002353 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002354 if proxy:
2355 cmd += ['--proxy', proxy]
2356 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2357 cmd += ['--proxy', os.environ['http_proxy']]
2358 if srcUrl.startswith('persistent-https'):
2359 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2360 elif srcUrl.startswith('persistent-http'):
2361 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002362 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002363
Dave Borowitz137d0132015-01-02 11:12:54 -08002364 if IsTrace():
2365 Trace('%s', ' '.join(cmd))
2366 try:
2367 proc = subprocess.Popen(cmd)
2368 except OSError:
2369 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002370
Dave Borowitz137d0132015-01-02 11:12:54 -08002371 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002372
Dave Borowitz137d0132015-01-02 11:12:54 -08002373 if curlret == 22:
2374 # From curl man page:
2375 # 22: HTTP page not retrieved. The requested url was not found or
2376 # returned another error with the HTTP error code being 400 or above.
2377 # This return code only appears if -f, --fail is used.
2378 if not quiet:
2379 print("Server does not provide clone.bundle; ignoring.",
2380 file=sys.stderr)
2381 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002382
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002383 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002384 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002385 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002386 return True
2387 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002388 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002389 return False
2390 else:
2391 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002392
Kris Giesingc8d882a2014-12-23 13:02:32 -08002393 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002394 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002395 with open(path, 'rb') as f:
2396 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002397 return True
2398 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002399 if not quiet:
2400 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002401 return False
2402 except OSError:
2403 return False
2404
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002405 def _Checkout(self, rev, quiet=False):
2406 cmd = ['checkout']
2407 if quiet:
2408 cmd.append('-q')
2409 cmd.append(rev)
2410 cmd.append('--')
2411 if GitCommand(self, cmd).Wait() != 0:
2412 if self._allrefs:
2413 raise GitError('%s checkout %s ' % (self.name, rev))
2414
Anthony King7bdac712014-07-16 12:56:40 +01002415 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002416 cmd = ['cherry-pick']
2417 cmd.append(rev)
2418 cmd.append('--')
2419 if GitCommand(self, cmd).Wait() != 0:
2420 if self._allrefs:
2421 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2422
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302423 def _LsRemote(self, refs):
2424 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302425 p = GitCommand(self, cmd, capture_stdout=True)
2426 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002427 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302428 return None
2429
Anthony King7bdac712014-07-16 12:56:40 +01002430 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002431 cmd = ['revert']
2432 cmd.append('--no-edit')
2433 cmd.append(rev)
2434 cmd.append('--')
2435 if GitCommand(self, cmd).Wait() != 0:
2436 if self._allrefs:
2437 raise GitError('%s revert %s ' % (self.name, rev))
2438
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002439 def _ResetHard(self, rev, quiet=True):
2440 cmd = ['reset', '--hard']
2441 if quiet:
2442 cmd.append('-q')
2443 cmd.append(rev)
2444 if GitCommand(self, cmd).Wait() != 0:
2445 raise GitError('%s reset --hard %s ' % (self.name, rev))
2446
Martin Kellye4e94d22017-03-21 16:05:12 -07002447 def _SyncSubmodules(self, quiet=True):
2448 cmd = ['submodule', 'update', '--init', '--recursive']
2449 if quiet:
2450 cmd.append('-q')
2451 if GitCommand(self, cmd).Wait() != 0:
2452 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2453
Anthony King7bdac712014-07-16 12:56:40 +01002454 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002455 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002456 if onto is not None:
2457 cmd.extend(['--onto', onto])
2458 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002459 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002460 raise GitError('%s rebase %s ' % (self.name, upstream))
2461
Pierre Tardy3d125942012-05-04 12:18:12 +02002462 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002463 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002464 if ffonly:
2465 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002466 if GitCommand(self, cmd).Wait() != 0:
2467 raise GitError('%s merge %s ' % (self.name, head))
2468
Kevin Degiabaa7f32014-11-12 11:27:45 -07002469 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002470 init_git_dir = not os.path.exists(self.gitdir)
2471 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002472 try:
2473 # Initialize the bare repository, which contains all of the objects.
2474 if init_obj_dir:
2475 os.makedirs(self.objdir)
2476 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002477
Kevin Degib1a07b82015-07-27 13:33:43 -06002478 # If we have a separate directory to hold refs, initialize it as well.
2479 if self.objdir != self.gitdir:
2480 if init_git_dir:
2481 os.makedirs(self.gitdir)
2482
2483 if init_obj_dir or init_git_dir:
2484 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2485 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002486 try:
2487 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2488 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002489 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002490 print("Retrying clone after deleting %s" %
2491 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002492 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002493 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2494 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002495 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002496 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002497 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2498 except:
2499 raise e
2500 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002501
Kevin Degi384b3c52014-10-16 16:02:58 -06002502 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 mp = self.manifest.manifestProject
2504 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002505
Kevin Degib1a07b82015-07-27 13:33:43 -06002506 if ref_dir or mirror_git:
2507 if not mirror_git:
2508 mirror_git = os.path.join(ref_dir, self.name + '.git')
2509 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2510 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002511
Kevin Degib1a07b82015-07-27 13:33:43 -06002512 if os.path.exists(mirror_git):
2513 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002514
Kevin Degib1a07b82015-07-27 13:33:43 -06002515 elif os.path.exists(repo_git):
2516 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002517
Kevin Degib1a07b82015-07-27 13:33:43 -06002518 else:
2519 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002520
Kevin Degib1a07b82015-07-27 13:33:43 -06002521 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002522 if not os.path.isabs(ref_dir):
2523 # The alternate directory is relative to the object database.
2524 ref_dir = os.path.relpath(ref_dir,
2525 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002526 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2527 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002528
Kevin Degib1a07b82015-07-27 13:33:43 -06002529 self._UpdateHooks()
2530
2531 m = self.manifest.manifestProject.config
2532 for key in ['user.name', 'user.email']:
2533 if m.Has(key, include_defaults=False):
2534 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002535 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002536 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002537 if self.manifest.IsMirror:
2538 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002539 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002540 self.config.SetString('core.bare', None)
2541 except Exception:
2542 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002543 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002544 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002545 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002546 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002547
Jimmie Westera0444582012-10-24 13:44:42 +02002548 def _UpdateHooks(self):
2549 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002550 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002551
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002552 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002553 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002554 if not os.path.exists(hooks):
2555 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002556 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002557 name = os.path.basename(stock_hook)
2558
Victor Boivie65e0f352011-04-18 11:23:29 +02002559 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002560 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002561 # Don't install a Gerrit Code Review hook if this
2562 # project does not appear to use it for reviews.
2563 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002564 # Since the manifest project is one of those, but also
2565 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002566 continue
2567
2568 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002569 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002570 continue
2571 if os.path.exists(dst):
2572 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002573 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002574 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002575 _warn("%s: Not replacing locally modified %s hook",
2576 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002577 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002578 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002579 platform_utils.symlink(
2580 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002581 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002582 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002583 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002584 else:
2585 raise
2586
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002587 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002588 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002589 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002590 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002591 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002592 remote.review = self.remote.review
2593 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002594
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002595 if self.worktree:
2596 remote.ResetFetch(mirror=False)
2597 else:
2598 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002599 remote.Save()
2600
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002601 def _InitMRef(self):
2602 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002603 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002604
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002605 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002606 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002607
2608 def _InitAnyMRef(self, ref):
2609 cur = self.bare_ref.symref(ref)
2610
2611 if self.revisionId:
2612 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2613 msg = 'manifest set to %s' % self.revisionId
2614 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002615 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002616 else:
2617 remote = self.GetRemote(self.remote.name)
2618 dst = remote.ToLocal(self.revisionExpr)
2619 if cur != dst:
2620 msg = 'manifest set to %s' % self.revisionExpr
2621 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002622
Kevin Degi384b3c52014-10-16 16:02:58 -06002623 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002624 symlink_files = self.shareable_files[:]
2625 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002626 if share_refs:
2627 symlink_files += self.working_tree_files
2628 symlink_dirs += self.working_tree_dirs
2629 to_symlink = symlink_files + symlink_dirs
2630 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002631 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002632 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002633 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002634 # Fail if the links are pointing to the wrong place
2635 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002636 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002637 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002638 'work tree. If you\'re comfortable with the '
2639 'possibility of losing the work tree\'s git metadata,'
2640 ' use `repo sync --force-sync {0}` to '
2641 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002642
David James8d201162013-10-11 17:03:19 -07002643 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2644 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2645
2646 Args:
2647 gitdir: The bare git repository. Must already be initialized.
2648 dotgit: The repository you would like to initialize.
2649 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2650 Only one work tree can store refs under a given |gitdir|.
2651 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2652 This saves you the effort of initializing |dotgit| yourself.
2653 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002654 symlink_files = self.shareable_files[:]
2655 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002656 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002657 symlink_files += self.working_tree_files
2658 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002659 to_symlink = symlink_files + symlink_dirs
2660
2661 to_copy = []
2662 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002663 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002664
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002665 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002666 for name in set(to_copy).union(to_symlink):
2667 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002668 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002669 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002670
Kevin Degi384b3c52014-10-16 16:02:58 -06002671 if os.path.lexists(dst):
2672 continue
David James8d201162013-10-11 17:03:19 -07002673
2674 # If the source dir doesn't exist, create an empty dir.
2675 if name in symlink_dirs and not os.path.lexists(src):
2676 os.makedirs(src)
2677
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002678 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002679 platform_utils.symlink(
2680 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002681 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002682 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002683 shutil.copytree(src, dst)
2684 elif os.path.isfile(src):
2685 shutil.copy(src, dst)
2686
Conley Owens80b87fe2014-05-09 17:13:44 -07002687 # If the source file doesn't exist, ensure the destination
2688 # file doesn't either.
2689 if name in symlink_files and not os.path.lexists(src):
2690 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002691 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002692 except OSError:
2693 pass
2694
David James8d201162013-10-11 17:03:19 -07002695 except OSError as e:
2696 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002697 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002698 else:
2699 raise
2700
Martin Kellye4e94d22017-03-21 16:05:12 -07002701 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002702 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002703 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002704 try:
2705 if init_dotgit:
2706 os.makedirs(dotgit)
2707 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2708 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709
Kevin Degiabaa7f32014-11-12 11:27:45 -07002710 try:
2711 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2712 except GitError as e:
2713 if force_sync:
2714 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002715 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002716 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002717 except:
2718 raise e
2719 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002720
Kevin Degib1a07b82015-07-27 13:33:43 -06002721 if init_dotgit:
2722 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002723
Kevin Degib1a07b82015-07-27 13:33:43 -06002724 cmd = ['read-tree', '--reset', '-u']
2725 cmd.append('-v')
2726 cmd.append(HEAD)
2727 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002728 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002729
Martin Kellye4e94d22017-03-21 16:05:12 -07002730 if submodules:
2731 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002732 self._CopyAndLinkFiles()
2733 except Exception:
2734 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002735 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002736 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002737
Renaud Paquay788e9622017-01-27 11:41:12 -08002738 def _get_symlink_error_message(self):
2739 if platform_utils.isWindows():
2740 return ('Unable to create symbolic link. Please re-run the command as '
2741 'Administrator, or see '
2742 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2743 'for other options.')
2744 return 'filesystem must support symlinks'
2745
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002747 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002748
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002749 def _revlist(self, *args, **kw):
2750 a = []
2751 a.extend(args)
2752 a.append('--')
2753 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002754
2755 @property
2756 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002757 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002758
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002759 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002760 """Get logs between two revisions of this project."""
2761 comp = '..'
2762 if rev1:
2763 revs = [rev1]
2764 if rev2:
2765 revs.extend([comp, rev2])
2766 cmd = ['log', ''.join(revs)]
2767 out = DiffColoring(self.config)
2768 if out.is_on and color:
2769 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002770 if pretty_format is not None:
2771 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002772 if oneline:
2773 cmd.append('--oneline')
2774
2775 try:
2776 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2777 if log.Wait() == 0:
2778 return log.stdout
2779 except GitError:
2780 # worktree may not exist if groups changed for example. In that case,
2781 # try in gitdir instead.
2782 if not os.path.exists(self.worktree):
2783 return self.bare_git.log(*cmd[1:])
2784 else:
2785 raise
2786 return None
2787
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002788 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2789 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002790 """Get the list of logs from this revision to given revisionId"""
2791 logs = {}
2792 selfId = self.GetRevisionId(self._allrefs)
2793 toId = toProject.GetRevisionId(toProject._allrefs)
2794
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002795 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2796 pretty_format=pretty_format)
2797 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2798 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002799 return logs
2800
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002801 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002802
David James8d201162013-10-11 17:03:19 -07002803 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002804 self._project = project
2805 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002806 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808 def LsOthers(self):
2809 p = GitCommand(self._project,
2810 ['ls-files',
2811 '-z',
2812 '--others',
2813 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002814 bare=False,
David James8d201162013-10-11 17:03:19 -07002815 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002816 capture_stdout=True,
2817 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002818 if p.Wait() == 0:
2819 out = p.stdout
2820 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002821 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002822 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002823 return []
2824
2825 def DiffZ(self, name, *args):
2826 cmd = [name]
2827 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002828 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002829 cmd.extend(args)
2830 p = GitCommand(self._project,
2831 cmd,
David James8d201162013-10-11 17:03:19 -07002832 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002833 bare=False,
2834 capture_stdout=True,
2835 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836 try:
2837 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002838 if not hasattr(out, 'encode'):
2839 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840 r = {}
2841 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002842 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002843 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002844 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002845 info = next(out)
2846 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002847 except StopIteration:
2848 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002849
2850 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002851
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002852 def __init__(self, path, omode, nmode, oid, nid, state):
2853 self.path = path
2854 self.src_path = None
2855 self.old_mode = omode
2856 self.new_mode = nmode
2857 self.old_id = oid
2858 self.new_id = nid
2859
2860 if len(state) == 1:
2861 self.status = state
2862 self.level = None
2863 else:
2864 self.status = state[:1]
2865 self.level = state[1:]
2866 while self.level.startswith('0'):
2867 self.level = self.level[1:]
2868
2869 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002870 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002871 if info.status in ('R', 'C'):
2872 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002873 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002874 r[info.path] = info
2875 return r
2876 finally:
2877 p.Wait()
2878
2879 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002880 if self._bare:
2881 path = os.path.join(self._project.gitdir, HEAD)
2882 else:
2883 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002884 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002885 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002886 except IOError as e:
2887 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002888 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002889 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002890 finally:
2891 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302892 try:
2893 line = line.decode()
2894 except AttributeError:
2895 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002896 if line.startswith('ref: '):
2897 return line[5:-1]
2898 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002899
2900 def SetHead(self, ref, message=None):
2901 cmdv = []
2902 if message is not None:
2903 cmdv.extend(['-m', message])
2904 cmdv.append(HEAD)
2905 cmdv.append(ref)
2906 self.symbolic_ref(*cmdv)
2907
2908 def DetachHead(self, new, message=None):
2909 cmdv = ['--no-deref']
2910 if message is not None:
2911 cmdv.extend(['-m', message])
2912 cmdv.append(HEAD)
2913 cmdv.append(new)
2914 self.update_ref(*cmdv)
2915
2916 def UpdateRef(self, name, new, old=None,
2917 message=None,
2918 detach=False):
2919 cmdv = []
2920 if message is not None:
2921 cmdv.extend(['-m', message])
2922 if detach:
2923 cmdv.append('--no-deref')
2924 cmdv.append(name)
2925 cmdv.append(new)
2926 if old is not None:
2927 cmdv.append(old)
2928 self.update_ref(*cmdv)
2929
2930 def DeleteRef(self, name, old=None):
2931 if not old:
2932 old = self.rev_parse(name)
2933 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002934 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002935
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002936 def rev_list(self, *args, **kw):
2937 if 'format' in kw:
2938 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2939 else:
2940 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002941 cmdv.extend(args)
2942 p = GitCommand(self._project,
2943 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002944 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002945 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002946 capture_stdout=True,
2947 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002948 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002949 raise GitError('%s rev-list %s: %s' %
2950 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002951 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002952
2953 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002954 """Allow arbitrary git commands using pythonic syntax.
2955
2956 This allows you to do things like:
2957 git_obj.rev_parse('HEAD')
2958
2959 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2960 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002961 Any other positional arguments will be passed to the git command, and the
2962 following keyword arguments are supported:
2963 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002964
2965 Args:
2966 name: The name of the git command to call. Any '_' characters will
2967 be replaced with '-'.
2968
2969 Returns:
2970 A callable object that will try to call git with the named command.
2971 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002972 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002973
Dave Borowitz091f8932012-10-23 17:01:04 -07002974 def runner(*args, **kwargs):
2975 cmdv = []
2976 config = kwargs.pop('config', None)
2977 for k in kwargs:
2978 raise TypeError('%s() got an unexpected keyword argument %r'
2979 % (name, k))
2980 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002981 if not git_require((1, 7, 2)):
2982 raise ValueError('cannot set config on command line for %s()'
2983 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302984 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002985 cmdv.append('-c')
2986 cmdv.append('%s=%s' % (k, v))
2987 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002988 cmdv.extend(args)
2989 p = GitCommand(self._project,
2990 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002991 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002992 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002993 capture_stdout=True,
2994 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002995 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002996 raise GitError('%s %s: %s' %
2997 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002998 r = p.stdout
2999 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3000 return r[:-1]
3001 return r
3002 return runner
3003
3004
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003005class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003006
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003007 def __str__(self):
3008 return 'prior sync failed; rebase still in progress'
3009
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003010
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003011class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003012
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003013 def __str__(self):
3014 return 'contains uncommitted changes'
3015
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003016
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003017class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003018
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003019 def __init__(self, project, text):
3020 self.project = project
3021 self.text = text
3022
3023 def Print(self, syncbuf):
3024 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3025 syncbuf.out.nl()
3026
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003027
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003028class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003029
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003030 def __init__(self, project, why):
3031 self.project = project
3032 self.why = why
3033
3034 def Print(self, syncbuf):
3035 syncbuf.out.fail('error: %s/: %s',
3036 self.project.relpath,
3037 str(self.why))
3038 syncbuf.out.nl()
3039
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003040
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003041class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003042
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003043 def __init__(self, project, action):
3044 self.project = project
3045 self.action = action
3046
3047 def Run(self, syncbuf):
3048 out = syncbuf.out
3049 out.project('project %s/', self.project.relpath)
3050 out.nl()
3051 try:
3052 self.action()
3053 out.nl()
3054 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003055 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003056 out.nl()
3057 return False
3058
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003059
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003060class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003061
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003062 def __init__(self, config):
3063 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003064 self.project = self.printer('header', attr='bold')
3065 self.info = self.printer('info')
3066 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003067
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003068
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003069class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003070
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003071 def __init__(self, config, detach_head=False):
3072 self._messages = []
3073 self._failures = []
3074 self._later_queue1 = []
3075 self._later_queue2 = []
3076
3077 self.out = _SyncColoring(config)
3078 self.out.redirect(sys.stderr)
3079
3080 self.detach_head = detach_head
3081 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003082 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003083
3084 def info(self, project, fmt, *args):
3085 self._messages.append(_InfoMessage(project, fmt % args))
3086
3087 def fail(self, project, err=None):
3088 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003089 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003090
3091 def later1(self, project, what):
3092 self._later_queue1.append(_Later(project, what))
3093
3094 def later2(self, project, what):
3095 self._later_queue2.append(_Later(project, what))
3096
3097 def Finish(self):
3098 self._PrintMessages()
3099 self._RunLater()
3100 self._PrintMessages()
3101 return self.clean
3102
David Rileye0684ad2017-04-05 00:02:59 -07003103 def Recently(self):
3104 recent_clean = self.recent_clean
3105 self.recent_clean = True
3106 return recent_clean
3107
3108 def _MarkUnclean(self):
3109 self.clean = False
3110 self.recent_clean = False
3111
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003112 def _RunLater(self):
3113 for q in ['_later_queue1', '_later_queue2']:
3114 if not self._RunQueue(q):
3115 return
3116
3117 def _RunQueue(self, queue):
3118 for m in getattr(self, queue):
3119 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003120 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003121 return False
3122 setattr(self, queue, [])
3123 return True
3124
3125 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003126 if self._messages or self._failures:
3127 if os.isatty(2):
3128 self.out.write(progress.CSI_ERASE_LINE)
3129 self.out.write('\r')
3130
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003131 for m in self._messages:
3132 m.Print(self)
3133 for m in self._failures:
3134 m.Print(self)
3135
3136 self._messages = []
3137 self._failures = []
3138
3139
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003140class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003141
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003142 """A special project housed under .repo.
3143 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003144
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003145 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003146 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003147 manifest=manifest,
3148 name=name,
3149 gitdir=gitdir,
3150 objdir=gitdir,
3151 worktree=worktree,
3152 remote=RemoteSpec('origin'),
3153 relpath='.repo/%s' % name,
3154 revisionExpr='refs/heads/master',
3155 revisionId=None,
3156 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003157
3158 def PreSync(self):
3159 if self.Exists:
3160 cb = self.CurrentBranch
3161 if cb:
3162 base = self.GetBranch(cb).merge
3163 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003164 self.revisionExpr = base
3165 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003166
Martin Kelly224a31a2017-07-10 14:46:25 -07003167 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003168 """ Prepare MetaProject for manifest branch switch
3169 """
3170
3171 # detach and delete manifest branch, allowing a new
3172 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003173 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003174 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003175 syncbuf.Finish()
3176
3177 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003178 ['update-ref', '-d', 'refs/heads/default'],
3179 capture_stdout=True,
3180 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003181
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003182 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003183 def LastFetch(self):
3184 try:
3185 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3186 return os.path.getmtime(fh)
3187 except OSError:
3188 return 0
3189
3190 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003191 def HasChanges(self):
3192 """Has the remote received new commits not yet checked out?
3193 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003194 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003195 return False
3196
David Pursehouse8a68ff92012-09-24 12:15:13 +09003197 all_refs = self.bare_ref.all
3198 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003199 head = self.work_git.GetHead()
3200 if head.startswith(R_HEADS):
3201 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003202 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003203 except KeyError:
3204 head = None
3205
3206 if revid == head:
3207 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003208 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003209 return True
3210 return False