blob: 4385d0b37af3dfacffeb139f8d6cf02b1d54236f [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'Fetch' git implementation
3
4git fetcher support the SRC_URI with format of:
5SRC_URI = "git://some.host/somepath;OptionA=xxx;OptionB=xxx;..."
6
7Supported SRC_URI options are:
8
9- branch
10 The git branch to retrieve from. The default is "master"
11
12 This option also supports multiple branch fetching, with branches
13 separated by commas. In multiple branches case, the name option
14 must have the same number of names to match the branches, which is
15 used to specify the SRC_REV for the branch
16 e.g:
17 SRC_URI="git://some.host/somepath;branch=branchX,branchY;name=nameX,nameY"
18 SRCREV_nameX = "xxxxxxxxxxxxxxxxxxxx"
19 SRCREV_nameY = "YYYYYYYYYYYYYYYYYYYY"
20
21- tag
22 The git tag to retrieve. The default is "master"
23
24- protocol
25 The method to use to access the repository. Common options are "git",
26 "http", "https", "file", "ssh" and "rsync". The default is "git".
27
28- rebaseable
29 rebaseable indicates that the upstream git repo may rebase in the future,
30 and current revision may disappear from upstream repo. This option will
31 remind fetcher to preserve local cache carefully for future use.
32 The default value is "0", set rebaseable=1 for rebaseable git repo.
33
34- nocheckout
35 Don't checkout source code when unpacking. set this option for the recipe
36 who has its own routine to checkout code.
37 The default is "0", set nocheckout=1 if needed.
38
39- bareclone
40 Create a bare clone of the source code and don't checkout the source code
41 when unpacking. Set this option for the recipe who has its own routine to
42 checkout code and tracking branch requirements.
43 The default is "0", set bareclone=1 if needed.
44
45- nobranch
46 Don't check the SHA validation for branch. set this option for the recipe
Andrew Geissler517393d2023-01-13 08:55:19 -060047 referring to commit which is valid in any namespace (branch, tag, ...)
48 instead of branch.
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049 The default is "0", set nobranch=1 if needed.
50
Patrick Williamsc0f7c042017-02-23 20:41:17 -060051- usehead
Brad Bishop6e60e8b2018-02-01 10:27:11 -050052 For local git:// urls to use the current branch HEAD as the revision for use with
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 AUTOREV. Implies nobranch.
54
Patrick Williamsc124f4f2015-09-15 14:41:29 -050055"""
56
Brad Bishopc342db32019-05-15 21:57:59 -040057# Copyright (C) 2005 Richard Purdie
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058#
Brad Bishopc342db32019-05-15 21:57:59 -040059# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061
Brad Bishopd7bf8c12018-02-25 22:55:05 -050062import collections
Patrick Williamsd7e96312015-09-22 08:09:05 -050063import errno
Brad Bishopd7bf8c12018-02-25 22:55:05 -050064import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050065import os
66import re
Andrew Geissler4c19ea12020-10-27 13:52:24 -050067import shlex
Andrew Geissler5082cc72023-09-11 08:41:39 -040068import shutil
Brad Bishopd7bf8c12018-02-25 22:55:05 -050069import subprocess
70import tempfile
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071import bb
Patrick Williamsc0f7c042017-02-23 20:41:17 -060072import bb.progress
Andrew Geissler5199d832021-09-24 16:47:35 -050073from contextlib import contextmanager
Patrick Williamsc124f4f2015-09-15 14:41:29 -050074from bb.fetch2 import FetchMethod
75from bb.fetch2 import runfetchcmd
76from bb.fetch2 import logger
77
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078
Patrick Williams03907ee2022-05-01 06:28:52 -050079sha1_re = re.compile(r'^[0-9a-f]{40}$')
80slash_re = re.compile(r"/+")
81
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082class GitProgressHandler(bb.progress.LineFilterProgressHandler):
83 """Extract progress information from git output"""
84 def __init__(self, d):
85 self._buffer = ''
86 self._count = 0
87 super(GitProgressHandler, self).__init__(d)
88 # Send an initial progress event so the bar gets shown
89 self._fire_progress(-1)
90
91 def write(self, string):
92 self._buffer += string
93 stages = ['Counting objects', 'Compressing objects', 'Receiving objects', 'Resolving deltas']
94 stage_weights = [0.2, 0.05, 0.5, 0.25]
95 stagenum = 0
96 for i, stage in reversed(list(enumerate(stages))):
97 if stage in self._buffer:
98 stagenum = i
99 self._buffer = ''
100 break
101 self._status = stages[stagenum]
102 percs = re.findall(r'(\d+)%', string)
103 if percs:
104 progress = int(round((int(percs[-1]) * stage_weights[stagenum]) + (sum(stage_weights[:stagenum]) * 100)))
105 rates = re.findall(r'([\d.]+ [a-zA-Z]*/s+)', string)
106 if rates:
107 rate = rates[-1]
108 else:
109 rate = None
110 self.update(progress, rate)
111 else:
112 if stagenum == 0:
113 percs = re.findall(r': (\d+)', string)
114 if percs:
115 count = int(percs[-1])
116 if count > self._count:
117 self._count = count
118 self._fire_progress(-count)
119 super(GitProgressHandler, self).write(string)
120
121
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122class Git(FetchMethod):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400123 bitbake_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.join(os.path.abspath(__file__))), '..', '..', '..'))
124 make_shallow_path = os.path.join(bitbake_dir, 'bin', 'git-make-shallow')
125
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500126 """Class to fetch a module or modules from git repositories"""
127 def init(self, d):
128 pass
129
130 def supports(self, ud, d):
131 """
132 Check to see if a given url can be fetched with git.
133 """
134 return ud.type in ['git']
135
136 def supports_checksum(self, urldata):
137 return False
138
139 def urldata_init(self, ud, d):
140 """
141 init git specific variable within url data
142 so that the git method like latest_revision() can work
143 """
144 if 'protocol' in ud.parm:
145 ud.proto = ud.parm['protocol']
146 elif not ud.host:
147 ud.proto = 'file'
148 else:
149 ud.proto = "git"
Andrew Geissler595f6302022-01-24 19:11:47 +0000150 if ud.host == "github.com" and ud.proto == "git":
151 # github stopped supporting git protocol
152 # https://github.blog/2021-09-01-improving-git-protocol-security-github/#no-more-unauthenticated-git
153 ud.proto = "https"
154 bb.warn("URL: %s uses git protocol which is no longer supported by github. Please change to ;protocol=https in the url." % ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155
156 if not ud.proto in ('git', 'file', 'ssh', 'http', 'https', 'rsync'):
157 raise bb.fetch2.ParameterError("Invalid protocol type", ud.url)
158
159 ud.nocheckout = ud.parm.get("nocheckout","0") == "1"
160
161 ud.rebaseable = ud.parm.get("rebaseable","0") == "1"
162
163 ud.nobranch = ud.parm.get("nobranch","0") == "1"
164
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 # usehead implies nobranch
166 ud.usehead = ud.parm.get("usehead","0") == "1"
167 if ud.usehead:
168 if ud.proto != "file":
169 raise bb.fetch2.ParameterError("The usehead option is only for use with local ('protocol=file') git repositories", ud.url)
170 ud.nobranch = 1
171
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 # bareclone implies nocheckout
173 ud.bareclone = ud.parm.get("bareclone","0") == "1"
174 if ud.bareclone:
175 ud.nocheckout = 1
176
177 ud.unresolvedrev = {}
Andrew Geissler595f6302022-01-24 19:11:47 +0000178 branches = ud.parm.get("branch", "").split(',')
179 if branches == [""] and not ud.nobranch:
180 bb.warn("URL: %s does not set any branch parameter. The future default branch used by tools and repositories is uncertain and we will therefore soon require this is set in all git urls." % ud.url)
181 branches = ["master"]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182 if len(branches) != len(ud.names):
183 raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500184
Andrew Geisslerc926e172021-05-07 16:11:35 -0500185 ud.noshared = d.getVar("BB_GIT_NOSHARED") == "1"
186
187 ud.cloneflags = "-n"
188 if not ud.noshared:
189 ud.cloneflags += " -s"
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500190 if ud.bareclone:
191 ud.cloneflags += " --mirror"
192
193 ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1"
194 ud.shallow_extra_refs = (d.getVar("BB_GIT_SHALLOW_EXTRA_REFS") or "").split()
195
196 depth_default = d.getVar("BB_GIT_SHALLOW_DEPTH")
197 if depth_default is not None:
198 try:
199 depth_default = int(depth_default or 0)
200 except ValueError:
201 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
202 else:
203 if depth_default < 0:
204 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
205 else:
206 depth_default = 1
207 ud.shallow_depths = collections.defaultdict(lambda: depth_default)
208
Brad Bishop19323692019-04-05 15:28:33 -0400209 revs_default = d.getVar("BB_GIT_SHALLOW_REVS")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210 ud.shallow_revs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 ud.branches = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212 for pos, name in enumerate(ud.names):
213 branch = branches[pos]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214 ud.branches[name] = branch
215 ud.unresolvedrev[name] = branch
216
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500217 shallow_depth = d.getVar("BB_GIT_SHALLOW_DEPTH_%s" % name)
218 if shallow_depth is not None:
219 try:
220 shallow_depth = int(shallow_depth or 0)
221 except ValueError:
222 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
223 else:
224 if shallow_depth < 0:
225 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
226 ud.shallow_depths[name] = shallow_depth
227
228 revs = d.getVar("BB_GIT_SHALLOW_REVS_%s" % name)
229 if revs is not None:
230 ud.shallow_revs.extend(revs.split())
231 elif revs_default is not None:
232 ud.shallow_revs.extend(revs_default.split())
233
234 if (ud.shallow and
235 not ud.shallow_revs and
236 all(ud.shallow_depths[n] == 0 for n in ud.names)):
237 # Shallow disabled for this URL
238 ud.shallow = False
239
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600240 if ud.usehead:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600241 # When usehead is set let's associate 'HEAD' with the unresolved
242 # rev of this repository. This will get resolved into a revision
243 # later. If an actual revision happens to have also been provided
244 # then this setting will be overridden.
245 for name in ud.names:
246 ud.unresolvedrev[name] = 'HEAD'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600247
Patrick Williams2390b1b2022-11-03 13:47:49 -0500248 ud.basecmd = d.getVar("FETCHCMD_git") or "git -c gc.autoDetach=false -c core.pager=cat"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500250 write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0"
251 ud.write_tarballs = write_tarballs != "0" or ud.rebaseable
252 ud.write_shallow_tarballs = (d.getVar("BB_GENERATE_SHALLOW_TARBALLS") or write_tarballs) != "0"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500253
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500254 ud.setup_revisions(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500255
256 for name in ud.names:
Patrick Williams03907ee2022-05-01 06:28:52 -0500257 # Ensure any revision that doesn't look like a SHA-1 is translated into one
258 if not sha1_re.match(ud.revisions[name] or ''):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259 if ud.revisions[name]:
260 ud.unresolvedrev[name] = ud.revisions[name]
261 ud.revisions[name] = self.latest_revision(ud, d, name)
262
Andrew Geisslerc3d88e42020-10-02 09:45:00 -0500263 gitsrcname = '%s%s' % (ud.host.replace(':', '.'), ud.path.replace('/', '.').replace('*', '.').replace(' ','_'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 if gitsrcname.startswith('.'):
265 gitsrcname = gitsrcname[1:]
266
Patrick Williams03907ee2022-05-01 06:28:52 -0500267 # For a rebaseable git repo, it is necessary to keep a mirror tar ball
268 # per revision, so that even if the revision disappears from the
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269 # upstream repo in the future, the mirror will remain intact and still
Patrick Williams03907ee2022-05-01 06:28:52 -0500270 # contain the revision
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 if ud.rebaseable:
272 for name in ud.names:
273 gitsrcname = gitsrcname + '_' + ud.revisions[name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500275 dl_dir = d.getVar("DL_DIR")
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800276 gitdir = d.getVar("GITDIR") or (dl_dir + "/git2")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500277 ud.clonedir = os.path.join(gitdir, gitsrcname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 ud.localfile = ud.clonedir
279
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500280 mirrortarball = 'git2_%s.tar.gz' % gitsrcname
281 ud.fullmirror = os.path.join(dl_dir, mirrortarball)
282 ud.mirrortarballs = [mirrortarball]
283 if ud.shallow:
284 tarballname = gitsrcname
285 if ud.bareclone:
286 tarballname = "%s_bare" % tarballname
287
288 if ud.shallow_revs:
289 tarballname = "%s_%s" % (tarballname, "_".join(sorted(ud.shallow_revs)))
290
291 for name, revision in sorted(ud.revisions.items()):
292 tarballname = "%s_%s" % (tarballname, ud.revisions[name][:7])
293 depth = ud.shallow_depths[name]
294 if depth:
295 tarballname = "%s-%s" % (tarballname, depth)
296
297 shallow_refs = []
298 if not ud.nobranch:
299 shallow_refs.extend(ud.branches.values())
300 if ud.shallow_extra_refs:
301 shallow_refs.extend(r.replace('refs/heads/', '').replace('*', 'ALL') for r in ud.shallow_extra_refs)
302 if shallow_refs:
303 tarballname = "%s_%s" % (tarballname, "_".join(sorted(shallow_refs)).replace('/', '.'))
304
305 fetcher = self.__class__.__name__.lower()
306 ud.shallowtarball = '%sshallow_%s.tar.gz' % (fetcher, tarballname)
307 ud.fullshallow = os.path.join(dl_dir, ud.shallowtarball)
308 ud.mirrortarballs.insert(0, ud.shallowtarball)
309
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310 def localpath(self, ud, d):
311 return ud.clonedir
312
313 def need_update(self, ud, d):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800314 return self.clonedir_need_update(ud, d) or self.shallow_tarball_need_update(ud) or self.tarball_need_update(ud)
315
316 def clonedir_need_update(self, ud, d):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 if not os.path.exists(ud.clonedir):
318 return True
Brad Bishop64c979e2019-11-04 13:55:29 -0500319 if ud.shallow and ud.write_shallow_tarballs and self.clonedir_need_shallow_revs(ud, d):
320 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 return False
325
Brad Bishop64c979e2019-11-04 13:55:29 -0500326 def clonedir_need_shallow_revs(self, ud, d):
327 for rev in ud.shallow_revs:
328 try:
329 runfetchcmd('%s rev-parse -q --verify %s' % (ud.basecmd, rev), d, quiet=True, workdir=ud.clonedir)
330 except bb.fetch2.FetchError:
331 return rev
332 return None
333
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800334 def shallow_tarball_need_update(self, ud):
335 return ud.shallow and ud.write_shallow_tarballs and not os.path.exists(ud.fullshallow)
336
337 def tarball_need_update(self, ud):
338 return ud.write_tarballs and not os.path.exists(ud.fullmirror)
339
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340 def try_premirror(self, ud, d):
341 # If we don't do this, updating an existing checkout with only premirrors
342 # is not possible
Brad Bishop19323692019-04-05 15:28:33 -0400343 if bb.utils.to_boolean(d.getVar("BB_FETCH_PREMIRRORONLY")):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344 return True
345 if os.path.exists(ud.clonedir):
346 return False
347 return True
348
349 def download(self, ud, d):
350 """Fetch url"""
351
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500352 # A current clone is preferred to either tarball, a shallow tarball is
353 # preferred to an out of date clone, and a missing clone will use
354 # either tarball.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800355 if ud.shallow and os.path.exists(ud.fullshallow) and self.need_update(ud, d):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500356 ud.localpath = ud.fullshallow
357 return
Andrew Geissler78b72792022-06-14 06:47:25 -0500358 elif os.path.exists(ud.fullmirror) and self.need_update(ud, d):
359 if not os.path.exists(ud.clonedir):
360 bb.utils.mkdirhier(ud.clonedir)
361 runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir)
362 else:
363 tmpdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
364 runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=tmpdir)
365 fetch_cmd = "LANG=C %s fetch -f --progress %s " % (ud.basecmd, shlex.quote(tmpdir))
366 runfetchcmd(fetch_cmd, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500367 repourl = self._get_repo_url(ud)
368
Andrew Geissler5082cc72023-09-11 08:41:39 -0400369 needs_clone = False
370 if os.path.exists(ud.clonedir):
371 # The directory may exist, but not be the top level of a bare git
372 # repository in which case it needs to be deleted and re-cloned.
373 try:
374 # Since clones can be bare, use --absolute-git-dir instead of --show-toplevel
375 output = runfetchcmd("LANG=C %s rev-parse --absolute-git-dir" % ud.basecmd, d, workdir=ud.clonedir)
Andrew Geissler220dafd2023-10-04 10:18:08 -0500376 toplevel = output.rstrip()
Andrew Geissler5082cc72023-09-11 08:41:39 -0400377
Andrew Geissler220dafd2023-10-04 10:18:08 -0500378 if not bb.utils.path_is_descendant(toplevel, ud.clonedir):
379 logger.warning("Top level directory '%s' is not a descendant of '%s'. Re-cloning", toplevel, ud.clonedir)
Andrew Geissler5082cc72023-09-11 08:41:39 -0400380 needs_clone = True
381 except bb.fetch2.FetchError as e:
382 logger.warning("Unable to get top level for %s (not a git directory?): %s", ud.clonedir, e)
383 needs_clone = True
Andrew Geissler220dafd2023-10-04 10:18:08 -0500384 except FileNotFoundError as e:
385 logger.warning("%s", e)
386 needs_clone = True
Andrew Geissler5082cc72023-09-11 08:41:39 -0400387
388 if needs_clone:
389 shutil.rmtree(ud.clonedir)
390 else:
391 needs_clone = True
392
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393 # If the repo still doesn't exist, fallback to cloning it
Andrew Geissler5082cc72023-09-11 08:41:39 -0400394 if needs_clone:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500395 # We do this since git will use a "-l" option automatically for local urls where possible,
396 # but it doesn't work when git/objects is a symlink, only works when it is a directory.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500397 if repourl.startswith("file://"):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500398 repourl_path = repourl[7:]
399 objects = os.path.join(repourl_path, 'objects')
400 if os.path.isdir(objects) and not os.path.islink(objects):
401 repourl = repourl_path
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500402 clone_cmd = "LANG=C %s clone --bare --mirror %s %s --progress" % (ud.basecmd, shlex.quote(repourl), ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 if ud.proto.lower() != 'file':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 bb.fetch2.check_network_access(d, clone_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 progresshandler = GitProgressHandler(d)
406 runfetchcmd(clone_cmd, d, log=progresshandler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408 # Update the checkout if needed
Brad Bishop64c979e2019-11-04 13:55:29 -0500409 if self.clonedir_need_update(ud, d):
Brad Bishop6ef32652018-10-09 18:59:25 +0100410 output = runfetchcmd("%s remote" % ud.basecmd, d, quiet=True, workdir=ud.clonedir)
411 if "origin" in output:
412 runfetchcmd("%s remote rm origin" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500413
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500414 runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=ud.clonedir)
Andrew Geissler517393d2023-01-13 08:55:19 -0600415
416 if ud.nobranch:
417 fetch_cmd = "LANG=C %s fetch -f --progress %s refs/*:refs/*" % (ud.basecmd, shlex.quote(repourl))
418 else:
419 fetch_cmd = "LANG=C %s fetch -f --progress %s refs/heads/*:refs/heads/* refs/tags/*:refs/tags/*" % (ud.basecmd, shlex.quote(repourl))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420 if ud.proto.lower() != 'file':
421 bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600422 progresshandler = GitProgressHandler(d)
423 runfetchcmd(fetch_cmd, d, log=progresshandler, workdir=ud.clonedir)
424 runfetchcmd("%s prune-packed" % ud.basecmd, d, workdir=ud.clonedir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400425 runfetchcmd("%s pack-refs --all" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600426 runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500427 try:
428 os.unlink(ud.fullmirror)
429 except OSError as exc:
430 if exc.errno != errno.ENOENT:
431 raise
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800432
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600434 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435 raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
436
Brad Bishop64c979e2019-11-04 13:55:29 -0500437 if ud.shallow and ud.write_shallow_tarballs:
438 missing_rev = self.clonedir_need_shallow_revs(ud, d)
439 if missing_rev:
440 raise bb.fetch2.FetchError("Unable to find revision %s even from upstream" % missing_rev)
441
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600442 if self._contains_lfs(ud, d, ud.clonedir) and self._need_lfs(ud):
443 # Unpack temporary working copy, use it to run 'git checkout' to force pre-fetching
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000444 # of all LFS blobs needed at the srcrev.
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600445 #
446 # It would be nice to just do this inline here by running 'git-lfs fetch'
447 # on the bare clonedir, but that operation requires a working copy on some
448 # releases of Git LFS.
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600449 with tempfile.TemporaryDirectory(dir=d.getVar('DL_DIR')) as tmpdir:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600450 # Do the checkout. This implicitly involves a Git LFS fetch.
Andrew Geisslerc926e172021-05-07 16:11:35 -0500451 Git.unpack(self, ud, tmpdir, d)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600452
453 # Scoop up a copy of any stuff that Git LFS downloaded. Merge them into
454 # the bare clonedir.
455 #
456 # As this procedure is invoked repeatedly on incremental fetches as
457 # a recipe's SRCREV is bumped throughout its lifetime, this will
458 # result in a gradual accumulation of LFS blobs in <ud.clonedir>/lfs
459 # corresponding to all the blobs reachable from the different revs
460 # fetched across time.
461 #
462 # Only do this if the unpack resulted in a .git/lfs directory being
463 # created; this only happens if at least one blob needed to be
464 # downloaded.
465 if os.path.exists(os.path.join(tmpdir, "git", ".git", "lfs")):
466 runfetchcmd("tar -cf - lfs | tar -xf - -C %s" % ud.clonedir, d, workdir="%s/git/.git" % tmpdir)
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600467
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468 def build_mirror_data(self, ud, d):
Andrew Geissler5199d832021-09-24 16:47:35 -0500469
470 # Create as a temp file and move atomically into position to avoid races
471 @contextmanager
472 def create_atomic(filename):
473 fd, tfile = tempfile.mkstemp(dir=os.path.dirname(filename))
474 try:
475 yield tfile
476 umask = os.umask(0o666)
477 os.umask(umask)
478 os.chmod(tfile, (0o666 & ~umask))
479 os.rename(tfile, filename)
480 finally:
481 os.close(fd)
482
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500483 if ud.shallow and ud.write_shallow_tarballs:
484 if not os.path.exists(ud.fullshallow):
485 if os.path.islink(ud.fullshallow):
486 os.unlink(ud.fullshallow)
487 tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
488 shallowclone = os.path.join(tempdir, 'git')
489 try:
490 self.clone_shallow_local(ud, shallowclone, d)
491
492 logger.info("Creating tarball of git repository")
Andrew Geissler5199d832021-09-24 16:47:35 -0500493 with create_atomic(ud.fullshallow) as tfile:
494 runfetchcmd("tar -czf %s ." % tfile, d, workdir=shallowclone)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500495 runfetchcmd("touch %s.done" % ud.fullshallow, d)
496 finally:
497 bb.utils.remove(tempdir, recurse=True)
498 elif ud.write_tarballs and not os.path.exists(ud.fullmirror):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500499 if os.path.islink(ud.fullmirror):
500 os.unlink(ud.fullmirror)
501
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502 logger.info("Creating tarball of git repository")
Andrew Geissler5199d832021-09-24 16:47:35 -0500503 with create_atomic(ud.fullmirror) as tfile:
Andrew Geissler9aee5002022-03-30 16:27:02 +0000504 mtime = runfetchcmd("git log --all -1 --format=%cD", d,
505 quiet=True, workdir=ud.clonedir)
Patrick Williams03907ee2022-05-01 06:28:52 -0500506 runfetchcmd("tar -czf %s --owner oe:0 --group oe:0 --mtime \"%s\" ."
Andrew Geissler9aee5002022-03-30 16:27:02 +0000507 % (tfile, mtime), d, workdir=ud.clonedir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 runfetchcmd("touch %s.done" % ud.fullmirror, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500510 def clone_shallow_local(self, ud, dest, d):
511 """Clone the repo and make it shallow.
512
513 The upstream url of the new clone isn't set at this time, as it'll be
514 set correctly when unpacked."""
515 runfetchcmd("%s clone %s %s %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, dest), d)
516
517 to_parse, shallow_branches = [], []
518 for name in ud.names:
519 revision = ud.revisions[name]
520 depth = ud.shallow_depths[name]
521 if depth:
522 to_parse.append('%s~%d^{}' % (revision, depth - 1))
523
524 # For nobranch, we need a ref, otherwise the commits will be
525 # removed, and for non-nobranch, we truncate the branch to our
526 # srcrev, to avoid keeping unnecessary history beyond that.
527 branch = ud.branches[name]
528 if ud.nobranch:
529 ref = "refs/shallow/%s" % name
530 elif ud.bareclone:
531 ref = "refs/heads/%s" % branch
532 else:
533 ref = "refs/remotes/origin/%s" % branch
534
535 shallow_branches.append(ref)
536 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest)
537
538 # Map srcrev+depths to revisions
539 parsed_depths = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join(to_parse)), d, workdir=dest)
540
541 # Resolve specified revisions
542 parsed_revs = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join('"%s^{}"' % r for r in ud.shallow_revs)), d, workdir=dest)
543 shallow_revisions = parsed_depths.splitlines() + parsed_revs.splitlines()
544
545 # Apply extra ref wildcards
546 all_refs = runfetchcmd('%s for-each-ref "--format=%%(refname)"' % ud.basecmd,
547 d, workdir=dest).splitlines()
548 for r in ud.shallow_extra_refs:
549 if not ud.bareclone:
550 r = r.replace('refs/heads/', 'refs/remotes/origin/')
551
552 if '*' in r:
553 matches = filter(lambda a: fnmatch.fnmatchcase(a, r), all_refs)
554 shallow_branches.extend(matches)
555 else:
556 shallow_branches.append(r)
557
558 # Make the repository shallow
Brad Bishop316dfdd2018-06-25 12:45:53 -0400559 shallow_cmd = [self.make_shallow_path, '-s']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500560 for b in shallow_branches:
561 shallow_cmd.append('-r')
562 shallow_cmd.append(b)
563 shallow_cmd.extend(shallow_revisions)
564 runfetchcmd(subprocess.list2cmdline(shallow_cmd), d, workdir=dest)
565
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566 def unpack(self, ud, destdir, d):
567 """ unpack the downloaded src to destdir"""
568
Andrew Geissler595f6302022-01-24 19:11:47 +0000569 subdir = ud.parm.get("subdir")
570 subpath = ud.parm.get("subpath")
571 readpathspec = ""
572 def_destsuffix = "git/"
573
574 if subpath:
575 readpathspec = ":%s" % subpath
576 def_destsuffix = "%s/" % os.path.basename(subpath.rstrip('/'))
577
578 if subdir:
579 # If 'subdir' param exists, create a dir and use it as destination for unpack cmd
580 if os.path.isabs(subdir):
581 if not os.path.realpath(subdir).startswith(os.path.realpath(destdir)):
582 raise bb.fetch2.UnpackError("subdir argument isn't a subdirectory of unpack root %s" % destdir, ud.url)
583 destdir = subdir
584 else:
585 destdir = os.path.join(destdir, subdir)
586 def_destsuffix = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587
588 destsuffix = ud.parm.get("destsuffix", def_destsuffix)
589 destdir = ud.destdir = os.path.join(destdir, destsuffix)
590 if os.path.exists(destdir):
591 bb.utils.prunedir(destdir)
592
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600593 need_lfs = self._need_lfs(ud)
Brad Bishopa34c0302019-09-23 22:34:48 -0400594
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500595 if not need_lfs:
596 ud.basecmd = "GIT_LFS_SKIP_SMUDGE=1 " + ud.basecmd
597
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800598 source_found = False
599 source_error = []
600
Andrew Geissler87f5cff2022-09-30 13:13:31 -0500601 clonedir_is_up_to_date = not self.clonedir_need_update(ud, d)
602 if clonedir_is_up_to_date:
603 runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, destdir), d)
604 source_found = True
605 else:
606 source_error.append("clone directory not available or not up to date: " + ud.clonedir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800607
608 if not source_found:
609 if ud.shallow:
610 if os.path.exists(ud.fullshallow):
611 bb.utils.mkdirhier(destdir)
612 runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=destdir)
613 source_found = True
614 else:
615 source_error.append("shallow clone not available: " + ud.fullshallow)
616 else:
617 source_error.append("shallow clone not enabled")
618
619 if not source_found:
620 raise bb.fetch2.UnpackError("No up to date source found: " + "; ".join(source_error), ud.url)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500622 repourl = self._get_repo_url(ud)
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500623 runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, shlex.quote(repourl)), d, workdir=destdir)
Brad Bishopc342db32019-05-15 21:57:59 -0400624
625 if self._contains_lfs(ud, d, destdir):
Brad Bishop00e122a2019-10-05 11:10:57 -0400626 if need_lfs and not self._find_git_lfs(d):
627 raise bb.fetch2.FetchError("Repository %s has LFS content, install git-lfs on host to download (or set lfs=0 to ignore it)" % (repourl))
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500628 elif not need_lfs:
Brad Bishopa34c0302019-09-23 22:34:48 -0400629 bb.note("Repository %s has LFS content but it is not being fetched" % (repourl))
Brad Bishopc342db32019-05-15 21:57:59 -0400630
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631 if not ud.nocheckout:
Andrew Geissler595f6302022-01-24 19:11:47 +0000632 if subpath:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600633 runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d,
634 workdir=destdir)
635 runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636 elif not ud.nobranch:
637 branchname = ud.branches[ud.names[0]]
638 runfetchcmd("%s checkout -B %s %s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600639 ud.revisions[ud.names[0]]), d, workdir=destdir)
Andre Rosa49271d42017-09-07 11:15:55 +0200640 runfetchcmd("%s branch %s --set-upstream-to origin/%s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600641 branchname), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 runfetchcmd("%s checkout %s" % (ud.basecmd, ud.revisions[ud.names[0]]), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500644
645 return True
646
647 def clean(self, ud, d):
648 """ clean the git directory """
649
Brad Bishop19323692019-04-05 15:28:33 -0400650 to_remove = [ud.localpath, ud.fullmirror, ud.fullmirror + ".done"]
651 # The localpath is a symlink to clonedir when it is cloned from a
652 # mirror, so remove both of them.
653 if os.path.islink(ud.localpath):
654 clonedir = os.path.realpath(ud.localpath)
655 to_remove.append(clonedir)
656
657 for r in to_remove:
658 if os.path.exists(r):
659 bb.note('Removing %s' % r)
660 bb.utils.remove(r, True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661
662 def supports_srcrev(self):
663 return True
664
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 def _contains_ref(self, ud, d, name, wd):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666 cmd = ""
667 if ud.nobranch:
668 cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
669 ud.basecmd, ud.revisions[name])
670 else:
671 cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
672 ud.basecmd, ud.revisions[name], ud.branches[name])
673 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675 except bb.fetch2.FetchError:
676 return False
677 if len(output.split()) > 1:
678 raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output))
679 return output.split()[0] != "0"
680
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600681 def _need_lfs(self, ud):
682 return ud.parm.get("lfs", "1") == "1"
683
Brad Bishopc342db32019-05-15 21:57:59 -0400684 def _contains_lfs(self, ud, d, wd):
685 """
686 Check if the repository has 'lfs' (large file) content
687 """
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500688
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600689 # The bare clonedir doesn't use the remote names; it has the branch immediately.
690 if wd == ud.clonedir:
691 refname = ud.branches[ud.names[0]]
692 else:
693 refname = "origin/%s" % ud.branches[ud.names[0]]
694
695 cmd = "%s grep lfs %s:.gitattributes | wc -l" % (
696 ud.basecmd, refname)
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500697
Brad Bishopc342db32019-05-15 21:57:59 -0400698 try:
699 output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
700 if int(output) > 0:
701 return True
702 except (bb.fetch2.FetchError,ValueError):
703 pass
704 return False
705
Brad Bishop00e122a2019-10-05 11:10:57 -0400706 def _find_git_lfs(self, d):
707 """
708 Return True if git-lfs can be found, False otherwise.
709 """
710 import shutil
711 return shutil.which("git-lfs", path=d.getVar('PATH')) is not None
712
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500713 def _get_repo_url(self, ud):
714 """
715 Return the repository URL
716 """
Andrew Geissler6ce62a22020-11-30 19:58:47 -0600717 # Note that we do not support passwords directly in the git urls. There are several
718 # reasons. SRC_URI can be written out to things like buildhistory and people don't
719 # want to leak passwords like that. Its also all too easy to share metadata without
720 # removing the password. ssh keys, ~/.netrc and ~/.ssh/config files can be used as
721 # alternatives so we will not take patches adding password support here.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722 if ud.user:
723 username = ud.user + '@'
724 else:
725 username = ""
726 return "%s://%s%s%s" % (ud.proto, username, ud.host, ud.path)
727
728 def _revision_key(self, ud, d, name):
729 """
730 Return a unique key for the url
731 """
Andrew Geissler82c905d2020-04-13 13:39:40 -0500732 # Collapse adjacent slashes
Andrew Geissler82c905d2020-04-13 13:39:40 -0500733 return "git:" + ud.host + slash_re.sub(".", ud.path) + ud.unresolvedrev[name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734
735 def _lsremote(self, ud, d, search):
736 """
737 Run git ls-remote with the specified search string
738 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 # Prevent recursion e.g. in OE if SRCPV is in PV, PV is in WORKDIR,
740 # and WORKDIR is in PATH (as a result of RSS), our call to
741 # runfetchcmd() exports PATH so this function will get called again (!)
742 # In this scenario the return call of the function isn't actually
743 # important - WORKDIR isn't needed in PATH to call git ls-remote
744 # anyway.
745 if d.getVar('_BB_GIT_IN_LSREMOTE', False):
746 return ''
747 d.setVar('_BB_GIT_IN_LSREMOTE', '1')
748 try:
749 repourl = self._get_repo_url(ud)
Andrew Geissler4c19ea12020-10-27 13:52:24 -0500750 cmd = "%s ls-remote %s %s" % \
751 (ud.basecmd, shlex.quote(repourl), search)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500752 if ud.proto.lower() != 'file':
753 bb.fetch2.check_network_access(d, cmd, repourl)
754 output = runfetchcmd(cmd, d, True)
755 if not output:
756 raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
757 finally:
758 d.delVar('_BB_GIT_IN_LSREMOTE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 return output
760
761 def _latest_revision(self, ud, d, name):
762 """
763 Compute the HEAD revision for the url
764 """
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500765 if not d.getVar("__BBSRCREV_SEEN"):
Andrew Geissler615f2f12022-07-15 14:00:58 -0500766 raise bb.fetch2.FetchError("Recipe uses a floating tag/branch '%s' for repo '%s' without a fixed SRCREV yet doesn't call bb.fetch2.get_srcrev() (use SRCPV in PV for OE)." % (ud.unresolvedrev[name], ud.host+ud.path))
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000767
768 # Ensure we mark as not cached
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500769 bb.fetch2.mark_recipe_nocache(d)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000770
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771 output = self._lsremote(ud, d, "")
772 # Tags of the form ^{} may not work, need to fallback to other form
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 if ud.unresolvedrev[name][:5] == "refs/" or ud.usehead:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774 head = ud.unresolvedrev[name]
775 tag = ud.unresolvedrev[name]
776 else:
777 head = "refs/heads/%s" % ud.unresolvedrev[name]
778 tag = "refs/tags/%s" % ud.unresolvedrev[name]
779 for s in [head, tag + "^{}", tag]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 for l in output.strip().split('\n'):
781 sha1, ref = l.split()
782 if s == ref:
783 return sha1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784 raise bb.fetch2.FetchError("Unable to resolve '%s' in upstream git repository in git ls-remote output for %s" % \
785 (ud.unresolvedrev[name], ud.host+ud.path))
786
787 def latest_versionstring(self, ud, d):
788 """
789 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
790 by searching through the tags output of ls-remote, comparing
791 versions and returning the highest match.
792 """
793 pupver = ('', '')
794
Brad Bishop19323692019-04-05 15:28:33 -0400795 tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX') or r"(?P<pver>([0-9][\.|_]?)+)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796 try:
797 output = self._lsremote(ud, d, "refs/tags/*")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400798 except (bb.fetch2.FetchError, bb.fetch2.NetworkAccess) as e:
799 bb.note("Could not list remote: %s" % str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800 return pupver
801
802 verstring = ""
803 revision = ""
804 for line in output.split("\n"):
805 if not line:
806 break
807
808 tag_head = line.split("/")[-1]
809 # Ignore non-released branches
Brad Bishop19323692019-04-05 15:28:33 -0400810 m = re.search(r"(alpha|beta|rc|final)+", tag_head)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500811 if m:
812 continue
813
814 # search for version in the line
815 tag = tagregex.search(tag_head)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500816 if tag is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 continue
818
819 tag = tag.group('pver')
820 tag = tag.replace("_", ".")
821
822 if verstring and bb.utils.vercmp(("0", tag, ""), ("0", verstring, "")) < 0:
823 continue
824
825 verstring = tag
826 revision = line.split()[0]
827 pupver = (verstring, revision)
828
829 return pupver
830
831 def _build_revision(self, ud, d, name):
832 return ud.revisions[name]
833
834 def gitpkgv_revision(self, ud, d, name):
835 """
836 Return a sortable revision number by counting commits in the history
837 Based on gitpkgv.bblass in meta-openembedded
838 """
839 rev = self._build_revision(ud, d, name)
840 localpath = ud.localpath
841 rev_file = os.path.join(localpath, "oe-gitpkgv_" + rev)
842 if not os.path.exists(localpath):
843 commits = None
844 else:
845 if not os.path.exists(rev_file) or not os.path.getsize(rev_file):
846 from pipes import quote
847 commits = bb.fetch2.runfetchcmd(
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848 "git rev-list %s -- | wc -l" % quote(rev),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849 d, quiet=True).strip().lstrip('0')
850 if commits:
851 open(rev_file, "w").write("%d\n" % int(commits))
852 else:
853 commits = open(rev_file, "r").readline(128).strip()
854 if commits:
855 return False, "%s+%s" % (commits, rev[:7])
856 else:
857 return True, str(rev)
858
859 def checkstatus(self, fetch, ud, d):
860 try:
861 self._lsremote(ud, d, "")
862 return True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500863 except bb.fetch2.FetchError:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864 return False