blob: d12d4666590cae3193e7d098faa9292242f416f8 [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
Mike Frysinger3164d402019-11-11 05:40:22 -050061 with open(lock, 'w') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070063
64 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070065 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070066 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080067 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 raise
69
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
Shawn O. Pearce48244782009-04-16 08:25:57 -070071def _error(fmt, *args):
72 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070073 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
David Pursehousef33929d2015-08-24 14:39:14 +090076def _warn(fmt, *args):
77 msg = fmt % args
78 print('warn: %s' % msg, file=sys.stderr)
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081def not_rev(r):
82 return '^' + r
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080085def sq(r):
86 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080087
Jonathan Nieder93719792015-03-17 11:29:58 -070088_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
90
Jonathan Nieder93719792015-03-17 11:29:58 -070091def _ProjectHooks():
92 """List the hooks present in the 'hooks' directory.
93
94 These hooks are project hooks and are copied to the '.git/hooks' directory
95 of all subprojects.
96
97 This function caches the list of hooks (based on the contents of the
98 'repo/hooks' directory) on the first call.
99
100 Returns:
101 A list of absolute paths to all of the files in the hooks directory.
102 """
103 global _project_hook_list
104 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700105 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700106 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700107 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 return _project_hook_list
109
110
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700111class DownloadedChange(object):
112 _commit_cache = None
113
114 def __init__(self, project, base, change_id, ps_id, commit):
115 self.project = project
116 self.base = base
117 self.change_id = change_id
118 self.ps_id = ps_id
119 self.commit = commit
120
121 @property
122 def commits(self):
123 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700124 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
125 '--abbrev-commit',
126 '--pretty=oneline',
127 '--reverse',
128 '--date-order',
129 not_rev(self.base),
130 self.commit,
131 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700132 return self._commit_cache
133
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135class ReviewableBranch(object):
136 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400137 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400151 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
152 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
153 try:
154 self._commit_cache = self.project.bare_git.rev_list(*args)
155 except GitError:
156 # We weren't able to probe the commits for this branch. Was it tracking
157 # a branch that no longer exists? If so, return no commits. Otherwise,
158 # rethrow the error as we don't know what's going on.
159 if self.base_exists:
160 raise
161
162 self._commit_cache = []
163
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700164 return self._commit_cache
165
166 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 def unabbrev_commits(self):
168 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700169 for commit in self.project.bare_git.rev_list(not_rev(self.base),
170 R_HEADS + self.name,
171 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800172 r[commit[0:8]] = commit
173 return r
174
175 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 return self.project.bare_git.log('--pretty=format:%cd',
178 '-n', '1',
179 R_HEADS + self.name,
180 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181
Mike Frysinger6da17752019-09-11 18:43:17 -0400182 @property
183 def base_exists(self):
184 """Whether the branch we're tracking exists.
185
186 Normally it should, but sometimes branches we track can get deleted.
187 """
188 if self._base_exists is None:
189 try:
190 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
191 # If we're still here, the base branch exists.
192 self._base_exists = True
193 except GitError:
194 # If we failed to verify, the base branch doesn't exist.
195 self._base_exists = False
196
197 return self._base_exists
198
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700199 def UploadForReview(self, people,
200 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000201 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200202 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700203 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200204 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200205 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800206 validate_certs=True,
207 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800208 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700209 people,
Brian Harring435370c2012-07-28 15:37:04 -0700210 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000211 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700213 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200214 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200215 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800216 validate_certs=validate_certs,
217 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700219 def GetPublishedRefs(self):
220 refs = {}
221 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700222 self.branch.remote.SshReviewUrl(self.project.UserEmail),
223 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700224 for line in output.split('\n'):
225 try:
226 (sha, ref) = line.split()
227 refs[sha] = ref
228 except ValueError:
229 pass
230
231 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700236 def __init__(self, config):
237 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100238 self.project = self.printer('header', attr='bold')
239 self.branch = self.printer('header', attr='bold')
240 self.nobranch = self.printer('nobranch', fg='red')
241 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
Anthony King7bdac712014-07-16 12:56:40 +0100243 self.added = self.printer('added', fg='green')
244 self.changed = self.printer('changed', fg='red')
245 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246
247
248class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700249
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 def __init__(self, config):
251 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100252 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400253 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700255
Anthony King7bdac712014-07-16 12:56:40 +0100256class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700257
James W. Mills24c13082012-04-12 15:04:13 -0500258 def __init__(self, name, value, keep):
259 self.name = name
260 self.value = value
261 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700262
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700263
Anthony King7bdac712014-07-16 12:56:40 +0100264class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700265
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800266 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 self.src = src
268 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800269 self.abs_src = abssrc
270 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
272 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800273 src = self.abs_src
274 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700275 # copy file if it does not exist or is out of date
276 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
277 try:
278 # remove existing file first, since it might be read-only
279 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800280 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400281 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200282 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700283 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200284 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700285 shutil.copy(src, dest)
286 # make the file read-only
287 mode = os.stat(dest)[stat.ST_MODE]
288 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
289 os.chmod(dest, mode)
290 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700291 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700292
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700293
Anthony King7bdac712014-07-16 12:56:40 +0100294class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700295
Wink Saville4c426ef2015-06-03 08:05:17 -0700296 def __init__(self, git_worktree, src, dest, relsrc, absdest):
297 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500298 self.src = src
299 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700300 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500301 self.abs_dest = absdest
302
Wink Saville4c426ef2015-06-03 08:05:17 -0700303 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500304 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700305 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500306 try:
307 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800308 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800309 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500310 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700312 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500313 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700314 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500315 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700316 _error('Cannot link file %s to %s', relSrc, absDest)
317
318 def _Link(self):
319 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
320 on the src linking all of the files in the source in to the destination
321 directory.
322 """
323 # We use the absSrc to handle the situation where the current directory
324 # is not the root of the repo
325 absSrc = os.path.join(self.git_worktree, self.src)
326 if os.path.exists(absSrc):
327 # Entity exists so just a simple one to one link operation
328 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
329 else:
330 # Entity doesn't exist assume there is a wild card
331 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700332 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700333 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700334 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700335 else:
336 absSrcFiles = glob.glob(absSrc)
337 for absSrcFile in absSrcFiles:
338 # Create a releative path from source dir to destination dir
339 absSrcDir = os.path.dirname(absSrcFile)
340 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
341
342 # Get the source file name
343 srcFile = os.path.basename(absSrcFile)
344
345 # Now form the final full paths to srcFile. They will be
346 # absolute for the desintaiton and relative for the srouce.
347 absDest = os.path.join(absDestDir, srcFile)
348 relSrc = os.path.join(relSrcDir, srcFile)
349 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500350
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700351
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700352class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700353
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700354 def __init__(self,
355 name,
Anthony King7bdac712014-07-16 12:56:40 +0100356 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700357 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100358 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700359 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700360 orig_name=None,
361 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700362 self.name = name
363 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700364 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700365 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100366 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700367 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700368 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700369
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700370
Doug Anderson37282b42011-03-04 11:54:18 -0800371class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700372
Doug Anderson37282b42011-03-04 11:54:18 -0800373 """A RepoHook contains information about a script to run as a hook.
374
375 Hooks are used to run a python script before running an upload (for instance,
376 to run presubmit checks). Eventually, we may have hooks for other actions.
377
378 This shouldn't be confused with files in the 'repo/hooks' directory. Those
379 files are copied into each '.git/hooks' folder for each project. Repo-level
380 hooks are associated instead with repo actions.
381
382 Hooks are always python. When a hook is run, we will load the hook into the
383 interpreter and execute its main() function.
384 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700385
Doug Anderson37282b42011-03-04 11:54:18 -0800386 def __init__(self,
387 hook_type,
388 hooks_project,
389 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400390 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800391 abort_if_user_denies=False):
392 """RepoHook constructor.
393
394 Params:
395 hook_type: A string representing the type of hook. This is also used
396 to figure out the name of the file containing the hook. For
397 example: 'pre-upload'.
398 hooks_project: The project containing the repo hooks. If you have a
399 manifest, this is manifest.repo_hooks_project. OK if this is None,
400 which will make the hook a no-op.
401 topdir: Repo's top directory (the one containing the .repo directory).
402 Scripts will run with CWD as this directory. If you have a manifest,
403 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400404 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800405 abort_if_user_denies: If True, we'll throw a HookError() if the user
406 doesn't allow us to run the hook.
407 """
408 self._hook_type = hook_type
409 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400410 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800411 self._topdir = topdir
412 self._abort_if_user_denies = abort_if_user_denies
413
414 # Store the full path to the script for convenience.
415 if self._hooks_project:
416 self._script_fullpath = os.path.join(self._hooks_project.worktree,
417 self._hook_type + '.py')
418 else:
419 self._script_fullpath = None
420
421 def _GetHash(self):
422 """Return a hash of the contents of the hooks directory.
423
424 We'll just use git to do this. This hash has the property that if anything
425 changes in the directory we will return a different has.
426
427 SECURITY CONSIDERATION:
428 This hash only represents the contents of files in the hook directory, not
429 any other files imported or called by hooks. Changes to imported files
430 can change the script behavior without affecting the hash.
431
432 Returns:
433 A string representing the hash. This will always be ASCII so that it can
434 be printed to the user easily.
435 """
436 assert self._hooks_project, "Must have hooks to calculate their hash."
437
438 # We will use the work_git object rather than just calling GetRevisionId().
439 # That gives us a hash of the latest checked in version of the files that
440 # the user will actually be executing. Specifically, GetRevisionId()
441 # doesn't appear to change even if a user checks out a different version
442 # of the hooks repo (via git checkout) nor if a user commits their own revs.
443 #
444 # NOTE: Local (non-committed) changes will not be factored into this hash.
445 # I think this is OK, since we're really only worried about warning the user
446 # about upstream changes.
447 return self._hooks_project.work_git.rev_parse('HEAD')
448
449 def _GetMustVerb(self):
450 """Return 'must' if the hook is required; 'should' if not."""
451 if self._abort_if_user_denies:
452 return 'must'
453 else:
454 return 'should'
455
456 def _CheckForHookApproval(self):
457 """Check to see whether this hook has been approved.
458
Mike Frysinger40252c22016-08-15 21:23:44 -0400459 We'll accept approval of manifest URLs if they're using secure transports.
460 This way the user can say they trust the manifest hoster. For insecure
461 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800462
463 Note that we ask permission for each individual hook even though we use
464 the hash of all hooks when detecting changes. We'd like the user to be
465 able to approve / deny each hook individually. We only use the hash of all
466 hooks because there is no other easy way to detect changes to local imports.
467
468 Returns:
469 True if this hook is approved to run; False otherwise.
470
471 Raises:
472 HookError: Raised if the user doesn't approve and abort_if_user_denies
473 was passed to the consturctor.
474 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 if self._ManifestUrlHasSecureScheme():
476 return self._CheckForHookApprovalManifest()
477 else:
478 return self._CheckForHookApprovalHash()
479
480 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
481 changed_prompt):
482 """Check for approval for a particular attribute and hook.
483
484 Args:
485 subkey: The git config key under [repo.hooks.<hook_type>] to store the
486 last approved string.
487 new_val: The new value to compare against the last approved one.
488 main_prompt: Message to display to the user to ask for approval.
489 changed_prompt: Message explaining why we're re-asking for approval.
490
491 Returns:
492 True if this hook is approved to run; False otherwise.
493
494 Raises:
495 HookError: Raised if the user doesn't approve and abort_if_user_denies
496 was passed to the consturctor.
497 """
Doug Anderson37282b42011-03-04 11:54:18 -0800498 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400499 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800500
Mike Frysinger40252c22016-08-15 21:23:44 -0400501 # Get the last value that the user approved for this hook; may be None.
502 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800503
Mike Frysinger40252c22016-08-15 21:23:44 -0400504 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800505 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400506 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800507 # Approval matched. We're done.
508 return True
509 else:
510 # Give the user a reason why we're prompting, since they last told
511 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400512 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800513 else:
514 prompt = ''
515
516 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
517 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400518 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530519 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900520 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800521
522 # User is doing a one-time approval.
523 if response in ('y', 'yes'):
524 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400525 elif response == 'always':
526 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800527 return True
528
529 # For anything else, we'll assume no approval.
530 if self._abort_if_user_denies:
531 raise HookError('You must allow the %s hook or use --no-verify.' %
532 self._hook_type)
533
534 return False
535
Mike Frysinger40252c22016-08-15 21:23:44 -0400536 def _ManifestUrlHasSecureScheme(self):
537 """Check if the URI for the manifest is a secure transport."""
538 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
539 parse_results = urllib.parse.urlparse(self._manifest_url)
540 return parse_results.scheme in secure_schemes
541
542 def _CheckForHookApprovalManifest(self):
543 """Check whether the user has approved this manifest host.
544
545 Returns:
546 True if this hook is approved to run; False otherwise.
547 """
548 return self._CheckForHookApprovalHelper(
549 'approvedmanifest',
550 self._manifest_url,
551 'Run hook scripts from %s' % (self._manifest_url,),
552 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
553
554 def _CheckForHookApprovalHash(self):
555 """Check whether the user has approved the hooks repo.
556
557 Returns:
558 True if this hook is approved to run; False otherwise.
559 """
560 prompt = ('Repo %s run the script:\n'
561 ' %s\n'
562 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700563 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400564 return self._CheckForHookApprovalHelper(
565 'approvedhash',
566 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700567 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400568 'Scripts have changed since %s was allowed.' % (self._hook_type,))
569
Mike Frysingerf7c51602019-06-18 17:23:39 -0400570 @staticmethod
571 def _ExtractInterpFromShebang(data):
572 """Extract the interpreter used in the shebang.
573
574 Try to locate the interpreter the script is using (ignoring `env`).
575
576 Args:
577 data: The file content of the script.
578
579 Returns:
580 The basename of the main script interpreter, or None if a shebang is not
581 used or could not be parsed out.
582 """
583 firstline = data.splitlines()[:1]
584 if not firstline:
585 return None
586
587 # The format here can be tricky.
588 shebang = firstline[0].strip()
589 m = re.match(r'^#!\s*([^\s]+)(?:\s+([^\s]+))?', shebang)
590 if not m:
591 return None
592
593 # If the using `env`, find the target program.
594 interp = m.group(1)
595 if os.path.basename(interp) == 'env':
596 interp = m.group(2)
597
598 return interp
599
600 def _ExecuteHookViaReexec(self, interp, context, **kwargs):
601 """Execute the hook script through |interp|.
602
603 Note: Support for this feature should be dropped ~Jun 2021.
604
605 Args:
606 interp: The Python program to run.
607 context: Basic Python context to execute the hook inside.
608 kwargs: Arbitrary arguments to pass to the hook script.
609
610 Raises:
611 HookError: When the hooks failed for any reason.
612 """
613 # This logic needs to be kept in sync with _ExecuteHookViaImport below.
614 script = """
615import json, os, sys
616path = '''%(path)s'''
617kwargs = json.loads('''%(kwargs)s''')
618context = json.loads('''%(context)s''')
619sys.path.insert(0, os.path.dirname(path))
620data = open(path).read()
621exec(compile(data, path, 'exec'), context)
622context['main'](**kwargs)
623""" % {
624 'path': self._script_fullpath,
625 'kwargs': json.dumps(kwargs),
626 'context': json.dumps(context),
627 }
628
629 # We pass the script via stdin to avoid OS argv limits. It also makes
630 # unhandled exception tracebacks less verbose/confusing for users.
631 cmd = [interp, '-c', 'import sys; exec(sys.stdin.read())']
632 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
633 proc.communicate(input=script.encode('utf-8'))
634 if proc.returncode:
635 raise HookError('Failed to run %s hook.' % (self._hook_type,))
636
637 def _ExecuteHookViaImport(self, data, context, **kwargs):
638 """Execute the hook code in |data| directly.
639
640 Args:
641 data: The code of the hook to execute.
642 context: Basic Python context to execute the hook inside.
643 kwargs: Arbitrary arguments to pass to the hook script.
644
645 Raises:
646 HookError: When the hooks failed for any reason.
647 """
648 # Exec, storing global context in the context dict. We catch exceptions
649 # and convert to a HookError w/ just the failing traceback.
650 try:
651 exec(compile(data, self._script_fullpath, 'exec'), context)
652 except Exception:
653 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
654 (traceback.format_exc(), self._hook_type))
655
656 # Running the script should have defined a main() function.
657 if 'main' not in context:
658 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
659
660 # Call the main function in the hook. If the hook should cause the
661 # build to fail, it will raise an Exception. We'll catch that convert
662 # to a HookError w/ just the failing traceback.
663 try:
664 context['main'](**kwargs)
665 except Exception:
666 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
667 'above.' % (traceback.format_exc(), self._hook_type))
668
Doug Anderson37282b42011-03-04 11:54:18 -0800669 def _ExecuteHook(self, **kwargs):
670 """Actually execute the given hook.
671
672 This will run the hook's 'main' function in our python interpreter.
673
674 Args:
675 kwargs: Keyword arguments to pass to the hook. These are often specific
676 to the hook type. For instance, pre-upload hooks will contain
677 a project_list.
678 """
679 # Keep sys.path and CWD stashed away so that we can always restore them
680 # upon function exit.
681 orig_path = os.getcwd()
682 orig_syspath = sys.path
683
684 try:
685 # Always run hooks with CWD as topdir.
686 os.chdir(self._topdir)
687
688 # Put the hook dir as the first item of sys.path so hooks can do
689 # relative imports. We want to replace the repo dir as [0] so
690 # hooks can't import repo files.
691 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
692
Mike Frysingerf7c51602019-06-18 17:23:39 -0400693 # Initial global context for the hook to run within.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500694 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800695
Doug Anderson37282b42011-03-04 11:54:18 -0800696 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
697 # We don't actually want hooks to define their main with this argument--
698 # it's there to remind them that their hook should always take **kwargs.
699 # For instance, a pre-upload hook should be defined like:
700 # def main(project_list, **kwargs):
701 #
702 # This allows us to later expand the API without breaking old hooks.
703 kwargs = kwargs.copy()
704 kwargs['hook_should_take_kwargs'] = True
705
Mike Frysingerf7c51602019-06-18 17:23:39 -0400706 # See what version of python the hook has been written against.
707 data = open(self._script_fullpath).read()
708 interp = self._ExtractInterpFromShebang(data)
709 reexec = False
710 if interp:
711 prog = os.path.basename(interp)
712 if prog.startswith('python2') and sys.version_info.major != 2:
713 reexec = True
714 elif prog.startswith('python3') and sys.version_info.major == 2:
715 reexec = True
716
717 # Attempt to execute the hooks through the requested version of Python.
718 if reexec:
719 try:
720 self._ExecuteHookViaReexec(interp, context, **kwargs)
721 except OSError as e:
722 if e.errno == errno.ENOENT:
723 # We couldn't find the interpreter, so fallback to importing.
724 reexec = False
725 else:
726 raise
727
728 # Run the hook by importing directly.
729 if not reexec:
730 self._ExecuteHookViaImport(data, context, **kwargs)
Doug Anderson37282b42011-03-04 11:54:18 -0800731 finally:
732 # Restore sys.path and CWD.
733 sys.path = orig_syspath
734 os.chdir(orig_path)
735
736 def Run(self, user_allows_all_hooks, **kwargs):
737 """Run the hook.
738
739 If the hook doesn't exist (because there is no hooks project or because
740 this particular hook is not enabled), this is a no-op.
741
742 Args:
743 user_allows_all_hooks: If True, we will never prompt about running the
744 hook--we'll just assume it's OK to run it.
745 kwargs: Keyword arguments to pass to the hook. These are often specific
746 to the hook type. For instance, pre-upload hooks will contain
747 a project_list.
748
749 Raises:
750 HookError: If there was a problem finding the hook or the user declined
751 to run a required hook (from _CheckForHookApproval).
752 """
753 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700754 if ((not self._hooks_project) or (self._hook_type not in
755 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800756 return
757
758 # Bail with a nice error if we can't find the hook.
759 if not os.path.isfile(self._script_fullpath):
760 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
761
762 # Make sure the user is OK with running the hook.
763 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
764 return
765
766 # Run the hook with the same version of python we're using.
767 self._ExecuteHook(**kwargs)
768
769
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700770class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600771 # These objects can be shared between several working trees.
772 shareable_files = ['description', 'info']
773 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
774 # These objects can only be used by a single working tree.
775 working_tree_files = ['config', 'packed-refs', 'shallow']
776 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700777
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700778 def __init__(self,
779 manifest,
780 name,
781 remote,
782 gitdir,
David James8d201162013-10-11 17:03:19 -0700783 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700784 worktree,
785 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700786 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800787 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100788 rebase=True,
789 groups=None,
790 sync_c=False,
791 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900792 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100793 clone_depth=None,
794 upstream=None,
795 parent=None,
796 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900797 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700798 optimized_fetch=False,
799 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800800 """Init a Project object.
801
802 Args:
803 manifest: The XmlManifest object.
804 name: The `name` attribute of manifest.xml's project element.
805 remote: RemoteSpec object specifying its remote's properties.
806 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700807 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800808 worktree: Absolute path of git working tree.
809 relpath: Relative path of git working tree to repo's top directory.
810 revisionExpr: The `revision` attribute of manifest.xml's project element.
811 revisionId: git commit id for checking out.
812 rebase: The `rebase` attribute of manifest.xml's project element.
813 groups: The `groups` attribute of manifest.xml's project element.
814 sync_c: The `sync-c` attribute of manifest.xml's project element.
815 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900816 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800817 upstream: The `upstream` attribute of manifest.xml's project element.
818 parent: The parent Project object.
819 is_derived: False if the project was explicitly defined in the manifest;
820 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400821 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900822 optimized_fetch: If True, when a project is set to a sha1 revision, only
823 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700824 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800825 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826 self.manifest = manifest
827 self.name = name
828 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800829 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700830 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800831 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700832 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800833 else:
834 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700835 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700836 self.revisionExpr = revisionExpr
837
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700838 if revisionId is None \
839 and revisionExpr \
840 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700841 self.revisionId = revisionExpr
842 else:
843 self.revisionId = revisionId
844
Mike Pontillod3153822012-02-28 11:53:24 -0800845 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700846 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700847 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800848 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900849 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900850 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700851 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800852 self.parent = parent
853 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900854 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800855 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800856
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700858 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500859 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500860 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700861 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
862 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700863
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800864 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700865 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800866 else:
867 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700868 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700869 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700870 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400871 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700872 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873
Doug Anderson37282b42011-03-04 11:54:18 -0800874 # This will be filled in if a project is later identified to be the
875 # project containing repo hooks.
876 self.enabled_repo_hooks = []
877
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700878 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800879 def Derived(self):
880 return self.is_derived
881
882 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700884 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885
886 @property
887 def CurrentBranch(self):
888 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400889
890 The branch name omits the 'refs/heads/' prefix.
891 None is returned if the project is on a detached HEAD, or if the work_git is
892 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700893 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400894 try:
895 b = self.work_git.GetHead()
896 except NoManifestException:
897 # If the local checkout is in a bad state, don't barf. Let the callers
898 # process this like the head is unreadable.
899 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700900 if b.startswith(R_HEADS):
901 return b[len(R_HEADS):]
902 return None
903
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700904 def IsRebaseInProgress(self):
905 w = self.worktree
906 g = os.path.join(w, '.git')
907 return os.path.exists(os.path.join(g, 'rebase-apply')) \
908 or os.path.exists(os.path.join(g, 'rebase-merge')) \
909 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200910
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911 def IsDirty(self, consider_untracked=True):
912 """Is the working directory modified in some way?
913 """
914 self.work_git.update_index('-q',
915 '--unmerged',
916 '--ignore-missing',
917 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900918 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700919 return True
920 if self.work_git.DiffZ('diff-files'):
921 return True
922 if consider_untracked and self.work_git.LsOthers():
923 return True
924 return False
925
926 _userident_name = None
927 _userident_email = None
928
929 @property
930 def UserName(self):
931 """Obtain the user's personal name.
932 """
933 if self._userident_name is None:
934 self._LoadUserIdentity()
935 return self._userident_name
936
937 @property
938 def UserEmail(self):
939 """Obtain the user's email address. This is very likely
940 to be their Gerrit login.
941 """
942 if self._userident_email is None:
943 self._LoadUserIdentity()
944 return self._userident_email
945
946 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900947 u = self.bare_git.var('GIT_COMMITTER_IDENT')
948 m = re.compile("^(.*) <([^>]*)> ").match(u)
949 if m:
950 self._userident_name = m.group(1)
951 self._userident_email = m.group(2)
952 else:
953 self._userident_name = ''
954 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700955
956 def GetRemote(self, name):
957 """Get the configuration for a single remote.
958 """
959 return self.config.GetRemote(name)
960
961 def GetBranch(self, name):
962 """Get the configuration for a single branch.
963 """
964 return self.config.GetBranch(name)
965
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700966 def GetBranches(self):
967 """Get all existing local branches.
968 """
969 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900970 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700971 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700972
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530973 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700974 if name.startswith(R_HEADS):
975 name = name[len(R_HEADS):]
976 b = self.GetBranch(name)
977 b.current = name == current
978 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900979 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700980 heads[name] = b
981
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530982 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700983 if name.startswith(R_PUB):
984 name = name[len(R_PUB):]
985 b = heads.get(name)
986 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900987 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700988
989 return heads
990
Colin Cross5acde752012-03-28 20:15:45 -0700991 def MatchesGroups(self, manifest_groups):
992 """Returns true if the manifest groups specified at init should cause
993 this project to be synced.
994 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700995 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700996
997 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700998 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700999 manifest_groups: "-group1,group2"
1000 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -05001001
1002 The special manifest group "default" will match any project that
1003 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -07001004 """
David Holmer0a1c6a12012-11-14 19:19:00 -05001005 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -07001006 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001007 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -05001008 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -07001009
Conley Owens971de8e2012-04-16 10:36:08 -07001010 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -07001011 for group in expanded_manifest_groups:
1012 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -07001013 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -07001014 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -07001015 matched = True
Colin Cross5acde752012-03-28 20:15:45 -07001016
Conley Owens971de8e2012-04-16 10:36:08 -07001017 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001019# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001020 def UncommitedFiles(self, get_all=True):
1021 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001023 Args:
1024 get_all: a boolean, if True - get information about all different
1025 uncommitted files. If False - return as soon as any kind of
1026 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001027 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001028 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001029 self.work_git.update_index('-q',
1030 '--unmerged',
1031 '--ignore-missing',
1032 '--refresh')
1033 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001034 details.append("rebase in progress")
1035 if not get_all:
1036 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001037
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001038 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
1039 if changes:
1040 details.extend(changes)
1041 if not get_all:
1042 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001043
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001044 changes = self.work_git.DiffZ('diff-files').keys()
1045 if changes:
1046 details.extend(changes)
1047 if not get_all:
1048 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001049
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001050 changes = self.work_git.LsOthers()
1051 if changes:
1052 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001053
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001054 return details
1055
1056 def HasChanges(self):
1057 """Returns true if there are uncommitted changes.
1058 """
1059 if self.UncommitedFiles(get_all=False):
1060 return True
1061 else:
1062 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001063
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001064 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +02001066
1067 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +02001068 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001069 quiet: If True then only print the project name. Do not print
1070 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 """
Renaud Paquaybed8b622018-09-27 10:46:58 -07001072 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001073 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +02001074 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -07001075 print(file=output_redir)
1076 print('project %s/' % self.relpath, file=output_redir)
1077 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078 return
1079
1080 self.work_git.update_index('-q',
1081 '--unmerged',
1082 '--ignore-missing',
1083 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001084 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
1086 df = self.work_git.DiffZ('diff-files')
1087 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +01001088 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001089 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090
1091 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001092 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +02001093 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -07001094 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001096 if quiet:
1097 out.nl()
1098 return 'DIRTY'
1099
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001100 branch = self.CurrentBranch
1101 if branch is None:
1102 out.nobranch('(*** NO BRANCH ***)')
1103 else:
1104 out.branch('branch %s', branch)
1105 out.nl()
1106
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001107 if rb:
1108 out.important('prior sync failed; rebase still in progress')
1109 out.nl()
1110
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001111 paths = list()
1112 paths.extend(di.keys())
1113 paths.extend(df.keys())
1114 paths.extend(do)
1115
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301116 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001117 try:
1118 i = di[p]
1119 except KeyError:
1120 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001121
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001122 try:
1123 f = df[p]
1124 except KeyError:
1125 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +02001126
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001127 if i:
1128 i_status = i.status.upper()
1129 else:
1130 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001131
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001132 if f:
1133 f_status = f.status.lower()
1134 else:
1135 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001136
1137 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001138 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001139 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001140 else:
1141 line = ' %s%s\t%s' % (i_status, f_status, p)
1142
1143 if i and not f:
1144 out.added('%s', line)
1145 elif (i and f) or (not i and f):
1146 out.changed('%s', line)
1147 elif not i and not f:
1148 out.untracked('%s', line)
1149 else:
1150 out.write('%s', line)
1151 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001152
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001153 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001154
pelyad67872d2012-03-28 14:49:58 +03001155 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001156 """Prints the status of the repository to stdout.
1157 """
1158 out = DiffColoring(self.config)
1159 cmd = ['diff']
1160 if out.is_on:
1161 cmd.append('--color')
1162 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001163 if absolute_paths:
1164 cmd.append('--src-prefix=a/%s/' % self.relpath)
1165 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001166 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -04001167 try:
1168 p = GitCommand(self,
1169 cmd,
1170 capture_stdout=True,
1171 capture_stderr=True)
1172 except GitError as e:
1173 out.nl()
1174 out.project('project %s/' % self.relpath)
1175 out.nl()
1176 out.fail('%s', str(e))
1177 out.nl()
1178 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001179 has_diff = False
1180 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -04001181 if not hasattr(line, 'encode'):
1182 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001183 if not has_diff:
1184 out.nl()
1185 out.project('project %s/' % self.relpath)
1186 out.nl()
1187 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001188 print(line[:-1])
Mike Frysinger0a9265e2019-09-30 23:59:27 -04001189 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001190
1191
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001192# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001193
David Pursehouse8a68ff92012-09-24 12:15:13 +09001194 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195 """Was the branch published (uploaded) for code review?
1196 If so, returns the SHA-1 hash of the last published
1197 state for the branch.
1198 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001199 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001200 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001201 try:
1202 return self.bare_git.rev_parse(key)
1203 except GitError:
1204 return None
1205 else:
1206 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001207 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001208 except KeyError:
1209 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001210
David Pursehouse8a68ff92012-09-24 12:15:13 +09001211 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001212 """Prunes any stale published refs.
1213 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001214 if all_refs is None:
1215 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001216 heads = set()
1217 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301218 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001219 if name.startswith(R_HEADS):
1220 heads.add(name)
1221 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001222 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001223
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301224 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001225 n = name[len(R_PUB):]
1226 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001227 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001228
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001229 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 """List any branches which can be uploaded for review.
1231 """
1232 heads = {}
1233 pubed = {}
1234
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301235 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001236 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001237 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001238 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001239 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001240
1241 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301242 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001243 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001244 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001245 if selected_branch and branch != selected_branch:
1246 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001247
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001248 rb = self.GetUploadableBranch(branch)
1249 if rb:
1250 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001251 return ready
1252
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001253 def GetUploadableBranch(self, branch_name):
1254 """Get a single uploadable branch, or None.
1255 """
1256 branch = self.GetBranch(branch_name)
1257 base = branch.LocalMerge
1258 if branch.LocalMerge:
1259 rb = ReviewableBranch(self, branch, base)
1260 if rb.commits:
1261 return rb
1262 return None
1263
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001264 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001265 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001266 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001267 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001268 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001269 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001270 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001271 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001272 validate_certs=True,
1273 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001274 """Uploads the named branch for code review.
1275 """
1276 if branch is None:
1277 branch = self.CurrentBranch
1278 if branch is None:
1279 raise GitError('not currently on a branch')
1280
1281 branch = self.GetBranch(branch)
1282 if not branch.LocalMerge:
1283 raise GitError('branch %s does not track a remote' % branch.name)
1284 if not branch.remote.review:
1285 raise GitError('remote %s has no review url' % branch.remote.name)
1286
Bryan Jacobsf609f912013-05-06 13:36:24 -04001287 if dest_branch is None:
1288 dest_branch = self.dest_branch
1289 if dest_branch is None:
1290 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291 if not dest_branch.startswith(R_HEADS):
1292 dest_branch = R_HEADS + dest_branch
1293
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001294 if not branch.remote.projectname:
1295 branch.remote.projectname = self.name
1296 branch.remote.Save()
1297
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001298 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001299 if url is None:
1300 raise UploadError('review not configured')
1301 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001302
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001303 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001304 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001305
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001306 for push_option in (push_options or []):
1307 cmd.append('-o')
1308 cmd.append(push_option)
1309
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001310 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001311
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001312 if dest_branch.startswith(R_HEADS):
1313 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001314
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001315 upload_type = 'for'
1316 if draft:
1317 upload_type = 'drafts'
1318
1319 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1320 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001321 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001322 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001323 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001324
David Pursehousef25a3702018-11-14 19:01:22 -08001325 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001326 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001327 if notify:
1328 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001329 if private:
1330 opts += ['private']
1331 if wip:
1332 opts += ['wip']
1333 if opts:
1334 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001335 cmd.append(ref_spec)
1336
Anthony King7bdac712014-07-16 12:56:40 +01001337 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001338 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001339
1340 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1341 self.bare_git.UpdateRef(R_PUB + branch.name,
1342 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001343 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001344
1345
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001346# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001347
Julien Campergue335f5ef2013-10-16 11:02:35 +02001348 def _ExtractArchive(self, tarpath, path=None):
1349 """Extract the given tar on its current location
1350
1351 Args:
1352 - tarpath: The path to the actual tar file
1353
1354 """
1355 try:
1356 with tarfile.open(tarpath, 'r') as tar:
1357 tar.extractall(path=path)
1358 return True
1359 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001360 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001361 return False
1362
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001363 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001364 quiet=False,
1365 is_new=None,
1366 current_branch_only=False,
1367 force_sync=False,
1368 clone_bundle=True,
1369 no_tags=False,
1370 archive=False,
1371 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001372 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001373 submodules=False,
1374 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001375 """Perform only the network IO portion of the sync process.
1376 Local working directory/branch state is not affected.
1377 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001378 if archive and not isinstance(self, MetaProject):
1379 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001380 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001381 return False
1382
1383 name = self.relpath.replace('\\', '/')
1384 name = name.replace('/', '_')
1385 tarpath = '%s.tar' % name
1386 topdir = self.manifest.topdir
1387
1388 try:
1389 self._FetchArchive(tarpath, cwd=topdir)
1390 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001391 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001392 return False
1393
1394 # From now on, we only need absolute tarpath
1395 tarpath = os.path.join(topdir, tarpath)
1396
1397 if not self._ExtractArchive(tarpath, path=topdir):
1398 return False
1399 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001400 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001401 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001402 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001403 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001404 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001405 if is_new is None:
1406 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001407 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001408 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001409 else:
1410 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001411 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001412
1413 if is_new:
1414 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1415 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001416 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001417 # This works for both absolute and relative alternate directories.
1418 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001419 except IOError:
1420 alt_dir = None
1421 else:
1422 alt_dir = None
1423
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001424 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001425 and alt_dir is None \
1426 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001427 is_new = False
1428
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001429 if not current_branch_only:
1430 if self.sync_c:
1431 current_branch_only = True
1432 elif not self.manifest._loaded:
1433 # Manifest cannot check defaults until it syncs.
1434 current_branch_only = False
1435 elif self.manifest.default.sync_c:
1436 current_branch_only = True
1437
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001438 if not no_tags:
1439 if not self.sync_tags:
1440 no_tags = True
1441
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001442 if self.clone_depth:
1443 depth = self.clone_depth
1444 else:
1445 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1446
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001447 need_to_fetch = not (optimized_fetch and
1448 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001449 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001450 if (need_to_fetch and
1451 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1452 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001453 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001454 submodules=submodules, force_sync=force_sync,
1455 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001456 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001457
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001458 mp = self.manifest.manifestProject
1459 dissociate = mp.config.GetBoolean('repo.dissociate')
1460 if dissociate:
1461 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1462 if os.path.exists(alternates_file):
1463 cmd = ['repack', '-a', '-d']
1464 if GitCommand(self, cmd, bare=True).Wait() != 0:
1465 return False
1466 platform_utils.remove(alternates_file)
1467
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001468 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001469 self._InitMRef()
1470 else:
1471 self._InitMirrorHead()
1472 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001473 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001474 except OSError:
1475 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001476 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001477
1478 def PostRepoUpgrade(self):
1479 self._InitHooks()
1480
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001481 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001482 if self.manifest.isGitcClient:
1483 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001484 for copyfile in self.copyfiles:
1485 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001486 for linkfile in self.linkfiles:
1487 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001488
Julien Camperguedd654222014-01-09 16:21:37 +01001489 def GetCommitRevisionId(self):
1490 """Get revisionId of a commit.
1491
1492 Use this method instead of GetRevisionId to get the id of the commit rather
1493 than the id of the current git object (for example, a tag)
1494
1495 """
1496 if not self.revisionExpr.startswith(R_TAGS):
1497 return self.GetRevisionId(self._allrefs)
1498
1499 try:
1500 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1501 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001502 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1503 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001504
David Pursehouse8a68ff92012-09-24 12:15:13 +09001505 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001506 if self.revisionId:
1507 return self.revisionId
1508
1509 rem = self.GetRemote(self.remote.name)
1510 rev = rem.ToLocal(self.revisionExpr)
1511
David Pursehouse8a68ff92012-09-24 12:15:13 +09001512 if all_refs is not None and rev in all_refs:
1513 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001514
1515 try:
1516 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1517 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001518 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1519 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001520
Martin Kellye4e94d22017-03-21 16:05:12 -07001521 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001522 """Perform only the local IO portion of the sync process.
1523 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001525 if not os.path.exists(self.gitdir):
1526 syncbuf.fail(self,
1527 'Cannot checkout %s due to missing network sync; Run '
1528 '`repo sync -n %s` first.' %
1529 (self.name, self.name))
1530 return
1531
Martin Kellye4e94d22017-03-21 16:05:12 -07001532 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001533 all_refs = self.bare_ref.all
1534 self.CleanPublishedCache(all_refs)
1535 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001536
David Pursehouse1d947b32012-10-25 12:23:11 +09001537 def _doff():
1538 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001539 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001540
Martin Kellye4e94d22017-03-21 16:05:12 -07001541 def _dosubmodules():
1542 self._SyncSubmodules(quiet=True)
1543
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001544 head = self.work_git.GetHead()
1545 if head.startswith(R_HEADS):
1546 branch = head[len(R_HEADS):]
1547 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001548 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001549 except KeyError:
1550 head = None
1551 else:
1552 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001554 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 # Currently on a detached HEAD. The user is assumed to
1556 # not have any local modifications worth worrying about.
1557 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001558 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001559 syncbuf.fail(self, _PriorSyncFailedError())
1560 return
1561
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001562 if head == revid:
1563 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001564 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001565 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001566 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001567 # The copy/linkfile config may have changed.
1568 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001569 return
1570 else:
1571 lost = self._revlist(not_rev(revid), HEAD)
1572 if lost:
1573 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001574
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001575 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001576 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001577 if submodules:
1578 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001579 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001580 syncbuf.fail(self, e)
1581 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001582 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001583 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001584
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001585 if head == revid:
1586 # No changes; don't do anything further.
1587 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001588 # The copy/linkfile config may have changed.
1589 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001590 return
1591
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001592 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001593
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001594 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001595 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001596 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001597 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001598 syncbuf.info(self,
1599 "leaving %s; does not track upstream",
1600 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001601 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001602 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001603 if submodules:
1604 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001605 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001606 syncbuf.fail(self, e)
1607 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001608 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001609 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001610
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001611 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001612
1613 # See if we can perform a fast forward merge. This can happen if our
1614 # branch isn't in the exact same state as we last published.
1615 try:
1616 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1617 # Skip the published logic.
1618 pub = False
1619 except GitError:
1620 pub = self.WasPublished(branch.name, all_refs)
1621
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001622 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001623 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001624 if not_merged:
1625 if upstream_gain:
1626 # The user has published this branch and some of those
1627 # commits are not yet merged upstream. We do not want
1628 # to rewrite the published commits so we punt.
1629 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001630 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001631 "branch %s is published (but not merged) and is now "
1632 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001633 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001634 elif pub == head:
1635 # All published commits are merged, and thus we are a
1636 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001637 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001638 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001639 if submodules:
1640 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001641 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001642
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001643 # Examine the local commits not in the remote. Find the
1644 # last one attributed to this user, if any.
1645 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001646 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001647 last_mine = None
1648 cnt_mine = 0
1649 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001650 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001651 if committer_email == self.UserEmail:
1652 last_mine = commit_id
1653 cnt_mine += 1
1654
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001655 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001656 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001657
1658 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001659 syncbuf.fail(self, _DirtyError())
1660 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001661
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001662 # If the upstream switched on us, warn the user.
1663 #
1664 if branch.merge != self.revisionExpr:
1665 if branch.merge and self.revisionExpr:
1666 syncbuf.info(self,
1667 'manifest switched %s...%s',
1668 branch.merge,
1669 self.revisionExpr)
1670 elif branch.merge:
1671 syncbuf.info(self,
1672 'manifest no longer tracks %s',
1673 branch.merge)
1674
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001675 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001676 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001677 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001678 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001679 syncbuf.info(self,
1680 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001681 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001682
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001683 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001684 if not ID_RE.match(self.revisionExpr):
1685 # in case of manifest sync the revisionExpr might be a SHA1
1686 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001687 if not branch.merge.startswith('refs/'):
1688 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001689 branch.Save()
1690
Mike Pontillod3153822012-02-28 11:53:24 -08001691 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001692 def _docopyandlink():
1693 self._CopyAndLinkFiles()
1694
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001695 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001696 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001697 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001698 if submodules:
1699 syncbuf.later2(self, _dosubmodules)
1700 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001701 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001702 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001703 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001704 if submodules:
1705 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001706 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001707 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001708 syncbuf.fail(self, e)
1709 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001710 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001711 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001712 if submodules:
1713 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001715 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001716 # dest should already be an absolute path, but src is project relative
1717 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001718 abssrc = os.path.join(self.worktree, src)
1719 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001720
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001721 def AddLinkFile(self, src, dest, absdest):
1722 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001723 # make src relative path to dest
1724 absdestdir = os.path.dirname(absdest)
1725 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001726 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001727
James W. Mills24c13082012-04-12 15:04:13 -05001728 def AddAnnotation(self, name, value, keep):
1729 self.annotations.append(_Annotation(name, value, keep))
1730
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001731 def DownloadPatchSet(self, change_id, patch_id):
1732 """Download a single patch set of a single change to FETCH_HEAD.
1733 """
1734 remote = self.GetRemote(self.remote.name)
1735
1736 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001737 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001738 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001739 if GitCommand(self, cmd, bare=True).Wait() != 0:
1740 return None
1741 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001742 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001743 change_id,
1744 patch_id,
1745 self.bare_git.rev_parse('FETCH_HEAD'))
1746
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001747
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001748# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001749
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001750 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001751 """Create a new branch off the manifest's revision.
1752 """
Simran Basib9a1b732015-08-20 12:19:28 -07001753 if not branch_merge:
1754 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001755 head = self.work_git.GetHead()
1756 if head == (R_HEADS + name):
1757 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758
David Pursehouse8a68ff92012-09-24 12:15:13 +09001759 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001760 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001761 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001762 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001763 capture_stdout=True,
1764 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001765
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001766 branch = self.GetBranch(name)
1767 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001768 branch.merge = branch_merge
1769 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1770 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001771
1772 if revision is None:
1773 revid = self.GetRevisionId(all_refs)
1774 else:
1775 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001776
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001777 if head.startswith(R_HEADS):
1778 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001779 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001780 except KeyError:
1781 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001782 if revid and head and revid == head:
1783 ref = os.path.join(self.gitdir, R_HEADS + name)
1784 try:
1785 os.makedirs(os.path.dirname(ref))
1786 except OSError:
1787 pass
1788 _lwrite(ref, '%s\n' % revid)
1789 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1790 'ref: %s%s\n' % (R_HEADS, name))
1791 branch.Save()
1792 return True
1793
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001794 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001795 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001796 capture_stdout=True,
1797 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001798 branch.Save()
1799 return True
1800 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001801
Wink Saville02d79452009-04-10 13:01:24 -07001802 def CheckoutBranch(self, name):
1803 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001804
1805 Args:
1806 name: The name of the branch to checkout.
1807
1808 Returns:
1809 True if the checkout succeeded; False if it didn't; None if the branch
1810 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001811 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001812 rev = R_HEADS + name
1813 head = self.work_git.GetHead()
1814 if head == rev:
1815 # Already on the branch
1816 #
1817 return True
Wink Saville02d79452009-04-10 13:01:24 -07001818
David Pursehouse8a68ff92012-09-24 12:15:13 +09001819 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001820 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001821 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001822 except KeyError:
1823 # Branch does not exist in this project
1824 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001825 return None
Wink Saville02d79452009-04-10 13:01:24 -07001826
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001827 if head.startswith(R_HEADS):
1828 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001829 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001830 except KeyError:
1831 head = None
1832
1833 if head == revid:
1834 # Same revision; just update HEAD to point to the new
1835 # target branch, but otherwise take no other action.
1836 #
1837 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1838 'ref: %s%s\n' % (R_HEADS, name))
1839 return True
1840
1841 return GitCommand(self,
1842 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001843 capture_stdout=True,
1844 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001845
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001846 def AbandonBranch(self, name):
1847 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001848
1849 Args:
1850 name: The name of the branch to abandon.
1851
1852 Returns:
1853 True if the abandon succeeded; False if it didn't; None if the branch
1854 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001855 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001856 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001857 all_refs = self.bare_ref.all
1858 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001859 # Doesn't exist
1860 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001861
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001862 head = self.work_git.GetHead()
1863 if head == rev:
1864 # We can't destroy the branch while we are sitting
1865 # on it. Switch to a detached HEAD.
1866 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001867 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001868
David Pursehouse8a68ff92012-09-24 12:15:13 +09001869 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001870 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001871 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1872 '%s\n' % revid)
1873 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001874 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001875
1876 return GitCommand(self,
1877 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001878 capture_stdout=True,
1879 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001880
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001881 def PruneHeads(self):
1882 """Prune any topic branches already merged into upstream.
1883 """
1884 cb = self.CurrentBranch
1885 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001886 left = self._allrefs
1887 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001888 if name.startswith(R_HEADS):
1889 name = name[len(R_HEADS):]
1890 if cb is None or name != cb:
1891 kill.append(name)
1892
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001893 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001894 if cb is not None \
1895 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001896 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001897 self.work_git.DetachHead(HEAD)
1898 kill.append(cb)
1899
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001900 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001901 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001902
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001903 try:
1904 self.bare_git.DetachHead(rev)
1905
1906 b = ['branch', '-d']
1907 b.extend(kill)
1908 b = GitCommand(self, b, bare=True,
1909 capture_stdout=True,
1910 capture_stderr=True)
1911 b.Wait()
1912 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001913 if ID_RE.match(old):
1914 self.bare_git.DetachHead(old)
1915 else:
1916 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001917 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001918
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001919 for branch in kill:
1920 if (R_HEADS + branch) not in left:
1921 self.CleanPublishedCache()
1922 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001923
1924 if cb and cb not in kill:
1925 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001926 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001927
1928 kept = []
1929 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001930 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001931 branch = self.GetBranch(branch)
1932 base = branch.LocalMerge
1933 if not base:
1934 base = rev
1935 kept.append(ReviewableBranch(self, branch, base))
1936 return kept
1937
1938
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001939# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001940
1941 def GetRegisteredSubprojects(self):
1942 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001943
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001944 def rec(subprojects):
1945 if not subprojects:
1946 return
1947 result.extend(subprojects)
1948 for p in subprojects:
1949 rec(p.subprojects)
1950 rec(self.subprojects)
1951 return result
1952
1953 def _GetSubmodules(self):
1954 # Unfortunately we cannot call `git submodule status --recursive` here
1955 # because the working tree might not exist yet, and it cannot be used
1956 # without a working tree in its current implementation.
1957
1958 def get_submodules(gitdir, rev):
1959 # Parse .gitmodules for submodule sub_paths and sub_urls
1960 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1961 if not sub_paths:
1962 return []
1963 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1964 # revision of submodule repository
1965 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1966 submodules = []
1967 for sub_path, sub_url in zip(sub_paths, sub_urls):
1968 try:
1969 sub_rev = sub_revs[sub_path]
1970 except KeyError:
1971 # Ignore non-exist submodules
1972 continue
1973 submodules.append((sub_rev, sub_path, sub_url))
1974 return submodules
1975
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001976 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1977 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001978
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001979 def parse_gitmodules(gitdir, rev):
1980 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1981 try:
Anthony King7bdac712014-07-16 12:56:40 +01001982 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1983 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001984 except GitError:
1985 return [], []
1986 if p.Wait() != 0:
1987 return [], []
1988
1989 gitmodules_lines = []
1990 fd, temp_gitmodules_path = tempfile.mkstemp()
1991 try:
1992 os.write(fd, p.stdout)
1993 os.close(fd)
1994 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001995 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1996 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001997 if p.Wait() != 0:
1998 return [], []
1999 gitmodules_lines = p.stdout.split('\n')
2000 except GitError:
2001 return [], []
2002 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08002003 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002004
2005 names = set()
2006 paths = {}
2007 urls = {}
2008 for line in gitmodules_lines:
2009 if not line:
2010 continue
2011 m = re_path.match(line)
2012 if m:
2013 names.add(m.group(1))
2014 paths[m.group(1)] = m.group(2)
2015 continue
2016 m = re_url.match(line)
2017 if m:
2018 names.add(m.group(1))
2019 urls[m.group(1)] = m.group(2)
2020 continue
2021 names = sorted(names)
2022 return ([paths.get(name, '') for name in names],
2023 [urls.get(name, '') for name in names])
2024
2025 def git_ls_tree(gitdir, rev, paths):
2026 cmd = ['ls-tree', rev, '--']
2027 cmd.extend(paths)
2028 try:
Anthony King7bdac712014-07-16 12:56:40 +01002029 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
2030 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002031 except GitError:
2032 return []
2033 if p.Wait() != 0:
2034 return []
2035 objects = {}
2036 for line in p.stdout.split('\n'):
2037 if not line.strip():
2038 continue
2039 object_rev, object_path = line.split()[2:4]
2040 objects[object_path] = object_rev
2041 return objects
2042
2043 try:
2044 rev = self.GetRevisionId()
2045 except GitError:
2046 return []
2047 return get_submodules(self.gitdir, rev)
2048
2049 def GetDerivedSubprojects(self):
2050 result = []
2051 if not self.Exists:
2052 # If git repo does not exist yet, querying its submodules will
2053 # mess up its states; so return here.
2054 return result
2055 for rev, path, url in self._GetSubmodules():
2056 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07002057 relpath, worktree, gitdir, objdir = \
2058 self.manifest.GetSubprojectPaths(self, name, path)
2059 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002060 if project:
2061 result.extend(project.GetDerivedSubprojects())
2062 continue
David James8d201162013-10-11 17:03:19 -07002063
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08002064 if url.startswith('..'):
2065 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002066 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01002067 url=url,
Steve Raed6480452016-08-10 15:00:00 -07002068 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01002069 review=self.remote.review,
2070 revision=self.remote.revision)
2071 subproject = Project(manifest=self.manifest,
2072 name=name,
2073 remote=remote,
2074 gitdir=gitdir,
2075 objdir=objdir,
2076 worktree=worktree,
2077 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02002078 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01002079 revisionId=rev,
2080 rebase=self.rebase,
2081 groups=self.groups,
2082 sync_c=self.sync_c,
2083 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09002084 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01002085 parent=self,
2086 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002087 result.append(subproject)
2088 result.extend(subproject.GetDerivedSubprojects())
2089 return result
2090
2091
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002092# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002093 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002094 try:
2095 # if revision (sha or tag) is not present then following function
2096 # throws an error.
2097 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2098 return True
2099 except GitError:
2100 # There is no such persistent revision. We have to fetch it.
2101 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002102
Julien Campergue335f5ef2013-10-16 11:02:35 +02002103 def _FetchArchive(self, tarpath, cwd=None):
2104 cmd = ['archive', '-v', '-o', tarpath]
2105 cmd.append('--remote=%s' % self.remote.url)
2106 cmd.append('--prefix=%s/' % self.relpath)
2107 cmd.append(self.revisionExpr)
2108
2109 command = GitCommand(self, cmd, cwd=cwd,
2110 capture_stdout=True,
2111 capture_stderr=True)
2112
2113 if command.Wait() != 0:
2114 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2115
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002116 def _RemoteFetch(self, name=None,
2117 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002118 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002119 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002120 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002121 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002122 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002123 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002124 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07002125 force_sync=False,
2126 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002127
2128 is_sha1 = False
2129 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002130 # The depth should not be used when fetching to a mirror because
2131 # it will result in a shallow repository that cannot be cloned or
2132 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002133 # The repo project should also never be synced with partial depth.
2134 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2135 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002136
Shawn Pearce69e04d82014-01-29 12:48:54 -08002137 if depth:
2138 current_branch_only = True
2139
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002140 if ID_RE.match(self.revisionExpr) is not None:
2141 is_sha1 = True
2142
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002143 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002144 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002145 # this is a tag and its sha1 value should never change
2146 tag_name = self.revisionExpr[len(R_TAGS):]
2147
2148 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002149 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002150 if not quiet:
2151 print('Skipped fetching project %s (already have persistent ref)'
2152 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002153 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002154 if is_sha1 and not depth:
2155 # When syncing a specific commit and --depth is not set:
2156 # * if upstream is explicitly specified and is not a sha1, fetch only
2157 # upstream as users expect only upstream to be fetch.
2158 # Note: The commit might not be in upstream in which case the sync
2159 # will fail.
2160 # * otherwise, fetch all branches to make sure we end up with the
2161 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002162 if self.upstream:
2163 current_branch_only = not ID_RE.match(self.upstream)
2164 else:
2165 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002166
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002167 if not name:
2168 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002169
2170 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002171 remote = self.GetRemote(name)
2172 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002173 ssh_proxy = True
2174
Shawn O. Pearce88443382010-10-08 10:02:09 +02002175 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002176 if alt_dir and 'objects' == os.path.basename(alt_dir):
2177 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002178 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2179 remote = self.GetRemote(name)
2180
David Pursehouse8a68ff92012-09-24 12:15:13 +09002181 all_refs = self.bare_ref.all
2182 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002183 tmp = set()
2184
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302185 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002186 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002187 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002188 all_refs[r] = ref_id
2189 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002190 continue
2191
David Pursehouse8a68ff92012-09-24 12:15:13 +09002192 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002193 continue
2194
David Pursehouse8a68ff92012-09-24 12:15:13 +09002195 r = 'refs/_alt/%s' % ref_id
2196 all_refs[r] = ref_id
2197 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002198 tmp.add(r)
2199
heping3d7bbc92017-04-12 19:51:47 +08002200 tmp_packed_lines = []
2201 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002202
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302203 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002204 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002205 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002206 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002207 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002208
heping3d7bbc92017-04-12 19:51:47 +08002209 tmp_packed = ''.join(tmp_packed_lines)
2210 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002211 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002212 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002213 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002214
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002215 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002216
Xin Li745be2e2019-06-03 11:24:30 -07002217 if clone_filter:
2218 git_require((2, 19, 0), fail=True, msg='partial clones')
2219 cmd.append('--filter=%s' % clone_filter)
2220 self.config.SetString('extensions.partialclone', self.remote.name)
2221
Conley Owensf97e8382015-01-21 11:12:46 -08002222 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002223 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002224 else:
2225 # If this repo has shallow objects, then we don't know which refs have
2226 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2227 # do this with projects that don't have shallow objects, since it is less
2228 # efficient.
2229 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2230 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002231
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002232 if quiet:
2233 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002234 if not self.worktree:
2235 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002236 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002237
Mike Frysingere57f1142019-03-18 21:27:54 -04002238 if force_sync:
2239 cmd.append('--force')
2240
David Pursehouse74cfd272015-10-14 10:50:15 +09002241 if prune:
2242 cmd.append('--prune')
2243
Martin Kellye4e94d22017-03-21 16:05:12 -07002244 if submodules:
2245 cmd.append('--recurse-submodules=on-demand')
2246
Kuang-che Wu6856f982019-11-25 12:37:55 +08002247 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002248 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002249 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002250 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002251 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002252 spec.append('tag')
2253 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002254
Kuang-che Wu6856f982019-11-25 12:37:55 +08002255 branch = self.revisionExpr
2256 if (not self.manifest.IsMirror and is_sha1 and depth
2257 and git_require((1, 8, 3))):
2258 # Shallow checkout of a specific commit, fetch from that commit and not
2259 # the heads only as the commit might be deeper in the history.
2260 spec.append(branch)
2261 else:
2262 if is_sha1:
2263 branch = self.upstream
2264 if branch is not None and branch.strip():
2265 if not branch.startswith('refs/'):
2266 branch = R_HEADS + branch
2267 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2268
2269 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2270 # whole repo.
2271 if self.manifest.IsMirror and not spec:
2272 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2273
2274 # If using depth then we should not get all the tags since they may
2275 # be outside of the depth.
2276 if no_tags or depth:
2277 cmd.append('--no-tags')
2278 else:
2279 cmd.append('--tags')
2280 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2281
Conley Owens80b87fe2014-05-09 17:13:44 -07002282 cmd.extend(spec)
2283
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002284 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002285 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002286 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002287 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002288 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002289 ok = True
2290 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002291 # If needed, run the 'git remote prune' the first time through the loop
2292 elif (not _i and
2293 "error:" in gitcmd.stderr and
2294 "git remote prune" in gitcmd.stderr):
2295 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002296 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002297 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002298 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002299 break
2300 continue
Brian Harring14a66742012-09-28 20:21:57 -07002301 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002302 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2303 # in sha1 mode, we just tried sync'ing from the upstream field; it
2304 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002305 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002306 elif ret < 0:
2307 # Git died with a signal, exit immediately
2308 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002309 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002310
2311 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002312 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002313 if old_packed != '':
2314 _lwrite(packed_refs, old_packed)
2315 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002316 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002317 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002318
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002319 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002320 # We just synced the upstream given branch; verify we
2321 # got what we wanted, else trigger a second run of all
2322 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002323 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002324 if current_branch_only and depth:
2325 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002326 return self._RemoteFetch(name=name,
2327 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002328 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002329 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002330 else:
2331 # Avoid infinite recursion: sync all branches with depth set to None
2332 return self._RemoteFetch(name=name, current_branch_only=False,
2333 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002334 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002335
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002336 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002337
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002338 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002339 if initial and \
2340 (self.manifest.manifestProject.config.GetString('repo.depth') or
2341 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002342 return False
2343
2344 remote = self.GetRemote(self.remote.name)
2345 bundle_url = remote.url + '/clone.bundle'
2346 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002347 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2348 'persistent-http',
2349 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002350 return False
2351
2352 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2353 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2354
2355 exist_dst = os.path.exists(bundle_dst)
2356 exist_tmp = os.path.exists(bundle_tmp)
2357
2358 if not initial and not exist_dst and not exist_tmp:
2359 return False
2360
2361 if not exist_dst:
2362 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2363 if not exist_dst:
2364 return False
2365
2366 cmd = ['fetch']
2367 if quiet:
2368 cmd.append('--quiet')
2369 if not self.worktree:
2370 cmd.append('--update-head-ok')
2371 cmd.append(bundle_dst)
2372 for f in remote.fetch:
2373 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002374 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002375
2376 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002377 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002378 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002379 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002380 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002381 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002382
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002383 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002384 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002385 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002386
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002387 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002388 if quiet:
2389 cmd += ['--silent']
2390 if os.path.exists(tmpPath):
2391 size = os.stat(tmpPath).st_size
2392 if size >= 1024:
2393 cmd += ['--continue-at', '%d' % (size,)]
2394 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002395 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002396 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002397 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002398 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002399 if proxy:
2400 cmd += ['--proxy', proxy]
2401 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2402 cmd += ['--proxy', os.environ['http_proxy']]
2403 if srcUrl.startswith('persistent-https'):
2404 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2405 elif srcUrl.startswith('persistent-http'):
2406 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002407 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002408
Dave Borowitz137d0132015-01-02 11:12:54 -08002409 if IsTrace():
2410 Trace('%s', ' '.join(cmd))
2411 try:
2412 proc = subprocess.Popen(cmd)
2413 except OSError:
2414 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002415
Dave Borowitz137d0132015-01-02 11:12:54 -08002416 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002417
Dave Borowitz137d0132015-01-02 11:12:54 -08002418 if curlret == 22:
2419 # From curl man page:
2420 # 22: HTTP page not retrieved. The requested url was not found or
2421 # returned another error with the HTTP error code being 400 or above.
2422 # This return code only appears if -f, --fail is used.
2423 if not quiet:
2424 print("Server does not provide clone.bundle; ignoring.",
2425 file=sys.stderr)
2426 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002427
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002428 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002429 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002430 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002431 return True
2432 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002433 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002434 return False
2435 else:
2436 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002437
Kris Giesingc8d882a2014-12-23 13:02:32 -08002438 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002439 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002440 with open(path, 'rb') as f:
2441 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002442 return True
2443 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002444 if not quiet:
2445 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002446 return False
2447 except OSError:
2448 return False
2449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002450 def _Checkout(self, rev, quiet=False):
2451 cmd = ['checkout']
2452 if quiet:
2453 cmd.append('-q')
2454 cmd.append(rev)
2455 cmd.append('--')
2456 if GitCommand(self, cmd).Wait() != 0:
2457 if self._allrefs:
2458 raise GitError('%s checkout %s ' % (self.name, rev))
2459
Anthony King7bdac712014-07-16 12:56:40 +01002460 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002461 cmd = ['cherry-pick']
2462 cmd.append(rev)
2463 cmd.append('--')
2464 if GitCommand(self, cmd).Wait() != 0:
2465 if self._allrefs:
2466 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2467
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302468 def _LsRemote(self, refs):
2469 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302470 p = GitCommand(self, cmd, capture_stdout=True)
2471 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002472 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302473 return None
2474
Anthony King7bdac712014-07-16 12:56:40 +01002475 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002476 cmd = ['revert']
2477 cmd.append('--no-edit')
2478 cmd.append(rev)
2479 cmd.append('--')
2480 if GitCommand(self, cmd).Wait() != 0:
2481 if self._allrefs:
2482 raise GitError('%s revert %s ' % (self.name, rev))
2483
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002484 def _ResetHard(self, rev, quiet=True):
2485 cmd = ['reset', '--hard']
2486 if quiet:
2487 cmd.append('-q')
2488 cmd.append(rev)
2489 if GitCommand(self, cmd).Wait() != 0:
2490 raise GitError('%s reset --hard %s ' % (self.name, rev))
2491
Martin Kellye4e94d22017-03-21 16:05:12 -07002492 def _SyncSubmodules(self, quiet=True):
2493 cmd = ['submodule', 'update', '--init', '--recursive']
2494 if quiet:
2495 cmd.append('-q')
2496 if GitCommand(self, cmd).Wait() != 0:
2497 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2498
Anthony King7bdac712014-07-16 12:56:40 +01002499 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002500 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002501 if onto is not None:
2502 cmd.extend(['--onto', onto])
2503 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002504 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002505 raise GitError('%s rebase %s ' % (self.name, upstream))
2506
Pierre Tardy3d125942012-05-04 12:18:12 +02002507 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002508 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002509 if ffonly:
2510 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002511 if GitCommand(self, cmd).Wait() != 0:
2512 raise GitError('%s merge %s ' % (self.name, head))
2513
Kevin Degiabaa7f32014-11-12 11:27:45 -07002514 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002515 init_git_dir = not os.path.exists(self.gitdir)
2516 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002517 try:
2518 # Initialize the bare repository, which contains all of the objects.
2519 if init_obj_dir:
2520 os.makedirs(self.objdir)
2521 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002522
Kevin Degib1a07b82015-07-27 13:33:43 -06002523 # If we have a separate directory to hold refs, initialize it as well.
2524 if self.objdir != self.gitdir:
2525 if init_git_dir:
2526 os.makedirs(self.gitdir)
2527
2528 if init_obj_dir or init_git_dir:
2529 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2530 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002531 try:
2532 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2533 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002534 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002535 print("Retrying clone after deleting %s" %
2536 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002537 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002538 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2539 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002540 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002541 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002542 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2543 except:
2544 raise e
2545 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002546
Kevin Degi384b3c52014-10-16 16:02:58 -06002547 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002548 mp = self.manifest.manifestProject
2549 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002550
Kevin Degib1a07b82015-07-27 13:33:43 -06002551 if ref_dir or mirror_git:
2552 if not mirror_git:
2553 mirror_git = os.path.join(ref_dir, self.name + '.git')
2554 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2555 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002556
Kevin Degib1a07b82015-07-27 13:33:43 -06002557 if os.path.exists(mirror_git):
2558 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002559
Kevin Degib1a07b82015-07-27 13:33:43 -06002560 elif os.path.exists(repo_git):
2561 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002562
Kevin Degib1a07b82015-07-27 13:33:43 -06002563 else:
2564 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002565
Kevin Degib1a07b82015-07-27 13:33:43 -06002566 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002567 if not os.path.isabs(ref_dir):
2568 # The alternate directory is relative to the object database.
2569 ref_dir = os.path.relpath(ref_dir,
2570 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002571 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2572 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002573
Kevin Degib1a07b82015-07-27 13:33:43 -06002574 self._UpdateHooks()
2575
2576 m = self.manifest.manifestProject.config
2577 for key in ['user.name', 'user.email']:
2578 if m.Has(key, include_defaults=False):
2579 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002580 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002581 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002582 if self.manifest.IsMirror:
2583 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002584 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002585 self.config.SetString('core.bare', None)
2586 except Exception:
2587 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002588 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002589 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002590 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002591 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002592
Jimmie Westera0444582012-10-24 13:44:42 +02002593 def _UpdateHooks(self):
2594 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002595 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002596
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002597 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002598 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002599 if not os.path.exists(hooks):
2600 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002601 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002602 name = os.path.basename(stock_hook)
2603
Victor Boivie65e0f352011-04-18 11:23:29 +02002604 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002605 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002606 # Don't install a Gerrit Code Review hook if this
2607 # project does not appear to use it for reviews.
2608 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002609 # Since the manifest project is one of those, but also
2610 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002611 continue
2612
2613 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002614 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002615 continue
2616 if os.path.exists(dst):
2617 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002618 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002619 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002620 _warn("%s: Not replacing locally modified %s hook",
2621 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002622 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002623 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002624 platform_utils.symlink(
2625 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002626 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002627 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002628 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002629 else:
2630 raise
2631
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002632 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002633 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002634 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002635 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002636 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002637 remote.review = self.remote.review
2638 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002639
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002640 if self.worktree:
2641 remote.ResetFetch(mirror=False)
2642 else:
2643 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002644 remote.Save()
2645
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002646 def _InitMRef(self):
2647 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002648 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002649
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002650 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002651 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002652
2653 def _InitAnyMRef(self, ref):
2654 cur = self.bare_ref.symref(ref)
2655
2656 if self.revisionId:
2657 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2658 msg = 'manifest set to %s' % self.revisionId
2659 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002660 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002661 else:
2662 remote = self.GetRemote(self.remote.name)
2663 dst = remote.ToLocal(self.revisionExpr)
2664 if cur != dst:
2665 msg = 'manifest set to %s' % self.revisionExpr
2666 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002667
Kevin Degi384b3c52014-10-16 16:02:58 -06002668 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002669 symlink_files = self.shareable_files[:]
2670 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002671 if share_refs:
2672 symlink_files += self.working_tree_files
2673 symlink_dirs += self.working_tree_dirs
2674 to_symlink = symlink_files + symlink_dirs
2675 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002676 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002677 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002678 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002679 # Fail if the links are pointing to the wrong place
2680 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002681 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002682 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002683 'work tree. If you\'re comfortable with the '
2684 'possibility of losing the work tree\'s git metadata,'
2685 ' use `repo sync --force-sync {0}` to '
2686 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002687
David James8d201162013-10-11 17:03:19 -07002688 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2689 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2690
2691 Args:
2692 gitdir: The bare git repository. Must already be initialized.
2693 dotgit: The repository you would like to initialize.
2694 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2695 Only one work tree can store refs under a given |gitdir|.
2696 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2697 This saves you the effort of initializing |dotgit| yourself.
2698 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002699 symlink_files = self.shareable_files[:]
2700 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002701 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002702 symlink_files += self.working_tree_files
2703 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002704 to_symlink = symlink_files + symlink_dirs
2705
2706 to_copy = []
2707 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002708 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002709
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002710 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002711 for name in set(to_copy).union(to_symlink):
2712 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002713 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002714 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002715
Kevin Degi384b3c52014-10-16 16:02:58 -06002716 if os.path.lexists(dst):
2717 continue
David James8d201162013-10-11 17:03:19 -07002718
2719 # If the source dir doesn't exist, create an empty dir.
2720 if name in symlink_dirs and not os.path.lexists(src):
2721 os.makedirs(src)
2722
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002723 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002724 platform_utils.symlink(
2725 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002726 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002727 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002728 shutil.copytree(src, dst)
2729 elif os.path.isfile(src):
2730 shutil.copy(src, dst)
2731
Conley Owens80b87fe2014-05-09 17:13:44 -07002732 # If the source file doesn't exist, ensure the destination
2733 # file doesn't either.
2734 if name in symlink_files and not os.path.lexists(src):
2735 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002736 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002737 except OSError:
2738 pass
2739
David James8d201162013-10-11 17:03:19 -07002740 except OSError as e:
2741 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002742 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002743 else:
2744 raise
2745
Martin Kellye4e94d22017-03-21 16:05:12 -07002746 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002747 realdotgit = os.path.join(self.worktree, '.git')
2748 tmpdotgit = realdotgit + '.tmp'
2749 init_dotgit = not os.path.exists(realdotgit)
2750 if init_dotgit:
2751 dotgit = tmpdotgit
2752 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2753 os.makedirs(tmpdotgit)
2754 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2755 copy_all=False)
2756 else:
2757 dotgit = realdotgit
2758
Kevin Degib1a07b82015-07-27 13:33:43 -06002759 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002760 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2761 except GitError as e:
2762 if force_sync and not init_dotgit:
2763 try:
2764 platform_utils.rmtree(dotgit)
2765 return self._InitWorkTree(force_sync=False, submodules=submodules)
2766 except:
2767 raise e
2768 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002769
Mike Frysingerf4545122019-11-11 04:34:16 -05002770 if init_dotgit:
2771 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002772
Mike Frysingerf4545122019-11-11 04:34:16 -05002773 # Now that the .git dir is fully set up, move it to its final home.
2774 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002775
Mike Frysingerf4545122019-11-11 04:34:16 -05002776 # Finish checking out the worktree.
2777 cmd = ['read-tree', '--reset', '-u']
2778 cmd.append('-v')
2779 cmd.append(HEAD)
2780 if GitCommand(self, cmd).Wait() != 0:
2781 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002782
Mike Frysingerf4545122019-11-11 04:34:16 -05002783 if submodules:
2784 self._SyncSubmodules(quiet=True)
2785 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002786
Renaud Paquay788e9622017-01-27 11:41:12 -08002787 def _get_symlink_error_message(self):
2788 if platform_utils.isWindows():
2789 return ('Unable to create symbolic link. Please re-run the command as '
2790 'Administrator, or see '
2791 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2792 'for other options.')
2793 return 'filesystem must support symlinks'
2794
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002795 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002796 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002797
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002798 def _revlist(self, *args, **kw):
2799 a = []
2800 a.extend(args)
2801 a.append('--')
2802 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002803
2804 @property
2805 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002806 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002808 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002809 """Get logs between two revisions of this project."""
2810 comp = '..'
2811 if rev1:
2812 revs = [rev1]
2813 if rev2:
2814 revs.extend([comp, rev2])
2815 cmd = ['log', ''.join(revs)]
2816 out = DiffColoring(self.config)
2817 if out.is_on and color:
2818 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002819 if pretty_format is not None:
2820 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002821 if oneline:
2822 cmd.append('--oneline')
2823
2824 try:
2825 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2826 if log.Wait() == 0:
2827 return log.stdout
2828 except GitError:
2829 # worktree may not exist if groups changed for example. In that case,
2830 # try in gitdir instead.
2831 if not os.path.exists(self.worktree):
2832 return self.bare_git.log(*cmd[1:])
2833 else:
2834 raise
2835 return None
2836
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002837 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2838 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002839 """Get the list of logs from this revision to given revisionId"""
2840 logs = {}
2841 selfId = self.GetRevisionId(self._allrefs)
2842 toId = toProject.GetRevisionId(toProject._allrefs)
2843
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002844 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2845 pretty_format=pretty_format)
2846 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2847 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002848 return logs
2849
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002850 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002851
David James8d201162013-10-11 17:03:19 -07002852 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002853 self._project = project
2854 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002855 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002856
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 def LsOthers(self):
2858 p = GitCommand(self._project,
2859 ['ls-files',
2860 '-z',
2861 '--others',
2862 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002863 bare=False,
David James8d201162013-10-11 17:03:19 -07002864 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002865 capture_stdout=True,
2866 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002867 if p.Wait() == 0:
2868 out = p.stdout
2869 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002870 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002871 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002872 return []
2873
2874 def DiffZ(self, name, *args):
2875 cmd = [name]
2876 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002877 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002878 cmd.extend(args)
2879 p = GitCommand(self._project,
2880 cmd,
David James8d201162013-10-11 17:03:19 -07002881 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002882 bare=False,
2883 capture_stdout=True,
2884 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002885 try:
2886 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002887 if not hasattr(out, 'encode'):
2888 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002889 r = {}
2890 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002891 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002893 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002894 info = next(out)
2895 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002896 except StopIteration:
2897 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002898
2899 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002900
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002901 def __init__(self, path, omode, nmode, oid, nid, state):
2902 self.path = path
2903 self.src_path = None
2904 self.old_mode = omode
2905 self.new_mode = nmode
2906 self.old_id = oid
2907 self.new_id = nid
2908
2909 if len(state) == 1:
2910 self.status = state
2911 self.level = None
2912 else:
2913 self.status = state[:1]
2914 self.level = state[1:]
2915 while self.level.startswith('0'):
2916 self.level = self.level[1:]
2917
2918 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002919 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002920 if info.status in ('R', 'C'):
2921 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002922 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002923 r[info.path] = info
2924 return r
2925 finally:
2926 p.Wait()
2927
2928 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002929 if self._bare:
2930 path = os.path.join(self._project.gitdir, HEAD)
2931 else:
2932 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002933 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002934 with open(path) as fd:
2935 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002936 except IOError as e:
2937 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002938 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302939 line = line.decode()
2940 except AttributeError:
2941 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002942 if line.startswith('ref: '):
2943 return line[5:-1]
2944 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002945
2946 def SetHead(self, ref, message=None):
2947 cmdv = []
2948 if message is not None:
2949 cmdv.extend(['-m', message])
2950 cmdv.append(HEAD)
2951 cmdv.append(ref)
2952 self.symbolic_ref(*cmdv)
2953
2954 def DetachHead(self, new, message=None):
2955 cmdv = ['--no-deref']
2956 if message is not None:
2957 cmdv.extend(['-m', message])
2958 cmdv.append(HEAD)
2959 cmdv.append(new)
2960 self.update_ref(*cmdv)
2961
2962 def UpdateRef(self, name, new, old=None,
2963 message=None,
2964 detach=False):
2965 cmdv = []
2966 if message is not None:
2967 cmdv.extend(['-m', message])
2968 if detach:
2969 cmdv.append('--no-deref')
2970 cmdv.append(name)
2971 cmdv.append(new)
2972 if old is not None:
2973 cmdv.append(old)
2974 self.update_ref(*cmdv)
2975
2976 def DeleteRef(self, name, old=None):
2977 if not old:
2978 old = self.rev_parse(name)
2979 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002980 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002981
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002982 def rev_list(self, *args, **kw):
2983 if 'format' in kw:
2984 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2985 else:
2986 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002987 cmdv.extend(args)
2988 p = GitCommand(self._project,
2989 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002990 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002991 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002992 capture_stdout=True,
2993 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002994 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002995 raise GitError('%s rev-list %s: %s' %
2996 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002997 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002998
2999 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003000 """Allow arbitrary git commands using pythonic syntax.
3001
3002 This allows you to do things like:
3003 git_obj.rev_parse('HEAD')
3004
3005 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3006 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003007 Any other positional arguments will be passed to the git command, and the
3008 following keyword arguments are supported:
3009 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003010
3011 Args:
3012 name: The name of the git command to call. Any '_' characters will
3013 be replaced with '-'.
3014
3015 Returns:
3016 A callable object that will try to call git with the named command.
3017 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003018 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003019
Dave Borowitz091f8932012-10-23 17:01:04 -07003020 def runner(*args, **kwargs):
3021 cmdv = []
3022 config = kwargs.pop('config', None)
3023 for k in kwargs:
3024 raise TypeError('%s() got an unexpected keyword argument %r'
3025 % (name, k))
3026 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07003027 if not git_require((1, 7, 2)):
3028 raise ValueError('cannot set config on command line for %s()'
3029 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303030 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003031 cmdv.append('-c')
3032 cmdv.append('%s=%s' % (k, v))
3033 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003034 cmdv.extend(args)
3035 p = GitCommand(self._project,
3036 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003037 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003038 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003039 capture_stdout=True,
3040 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003041 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003042 raise GitError('%s %s: %s' %
3043 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003044 r = p.stdout
3045 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3046 return r[:-1]
3047 return r
3048 return runner
3049
3050
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003051class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003052
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003053 def __str__(self):
3054 return 'prior sync failed; rebase still in progress'
3055
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003056
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003057class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003058
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003059 def __str__(self):
3060 return 'contains uncommitted changes'
3061
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003062
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003063class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003064
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003065 def __init__(self, project, text):
3066 self.project = project
3067 self.text = text
3068
3069 def Print(self, syncbuf):
3070 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3071 syncbuf.out.nl()
3072
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003073
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003074class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003075
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003076 def __init__(self, project, why):
3077 self.project = project
3078 self.why = why
3079
3080 def Print(self, syncbuf):
3081 syncbuf.out.fail('error: %s/: %s',
3082 self.project.relpath,
3083 str(self.why))
3084 syncbuf.out.nl()
3085
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003086
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003087class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003088
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003089 def __init__(self, project, action):
3090 self.project = project
3091 self.action = action
3092
3093 def Run(self, syncbuf):
3094 out = syncbuf.out
3095 out.project('project %s/', self.project.relpath)
3096 out.nl()
3097 try:
3098 self.action()
3099 out.nl()
3100 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003101 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003102 out.nl()
3103 return False
3104
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003105
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003107
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003108 def __init__(self, config):
3109 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003110 self.project = self.printer('header', attr='bold')
3111 self.info = self.printer('info')
3112 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003113
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003114
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003115class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003116
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003117 def __init__(self, config, detach_head=False):
3118 self._messages = []
3119 self._failures = []
3120 self._later_queue1 = []
3121 self._later_queue2 = []
3122
3123 self.out = _SyncColoring(config)
3124 self.out.redirect(sys.stderr)
3125
3126 self.detach_head = detach_head
3127 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003128 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003129
3130 def info(self, project, fmt, *args):
3131 self._messages.append(_InfoMessage(project, fmt % args))
3132
3133 def fail(self, project, err=None):
3134 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003135 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003136
3137 def later1(self, project, what):
3138 self._later_queue1.append(_Later(project, what))
3139
3140 def later2(self, project, what):
3141 self._later_queue2.append(_Later(project, what))
3142
3143 def Finish(self):
3144 self._PrintMessages()
3145 self._RunLater()
3146 self._PrintMessages()
3147 return self.clean
3148
David Rileye0684ad2017-04-05 00:02:59 -07003149 def Recently(self):
3150 recent_clean = self.recent_clean
3151 self.recent_clean = True
3152 return recent_clean
3153
3154 def _MarkUnclean(self):
3155 self.clean = False
3156 self.recent_clean = False
3157
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003158 def _RunLater(self):
3159 for q in ['_later_queue1', '_later_queue2']:
3160 if not self._RunQueue(q):
3161 return
3162
3163 def _RunQueue(self, queue):
3164 for m in getattr(self, queue):
3165 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003166 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003167 return False
3168 setattr(self, queue, [])
3169 return True
3170
3171 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003172 if self._messages or self._failures:
3173 if os.isatty(2):
3174 self.out.write(progress.CSI_ERASE_LINE)
3175 self.out.write('\r')
3176
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003177 for m in self._messages:
3178 m.Print(self)
3179 for m in self._failures:
3180 m.Print(self)
3181
3182 self._messages = []
3183 self._failures = []
3184
3185
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003186class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003187
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003188 """A special project housed under .repo.
3189 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003190
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003191 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003192 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003193 manifest=manifest,
3194 name=name,
3195 gitdir=gitdir,
3196 objdir=gitdir,
3197 worktree=worktree,
3198 remote=RemoteSpec('origin'),
3199 relpath='.repo/%s' % name,
3200 revisionExpr='refs/heads/master',
3201 revisionId=None,
3202 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003203
3204 def PreSync(self):
3205 if self.Exists:
3206 cb = self.CurrentBranch
3207 if cb:
3208 base = self.GetBranch(cb).merge
3209 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003210 self.revisionExpr = base
3211 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003212
Martin Kelly224a31a2017-07-10 14:46:25 -07003213 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003214 """ Prepare MetaProject for manifest branch switch
3215 """
3216
3217 # detach and delete manifest branch, allowing a new
3218 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003219 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003220 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003221 syncbuf.Finish()
3222
3223 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003224 ['update-ref', '-d', 'refs/heads/default'],
3225 capture_stdout=True,
3226 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003227
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003228 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003229 def LastFetch(self):
3230 try:
3231 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3232 return os.path.getmtime(fh)
3233 except OSError:
3234 return 0
3235
3236 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003237 def HasChanges(self):
3238 """Has the remote received new commits not yet checked out?
3239 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003240 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003241 return False
3242
David Pursehouse8a68ff92012-09-24 12:15:13 +09003243 all_refs = self.bare_ref.all
3244 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003245 head = self.work_git.GetHead()
3246 if head.startswith(R_HEADS):
3247 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003248 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003249 except KeyError:
3250 head = None
3251
3252 if revid == head:
3253 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003254 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003255 return True
3256 return False