sys-apps/i2c-tools: fix quoting on vars
[gentoo.git] / eclass / git-r3.eclass
1 # Copyright 1999-2018 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: git-r3.eclass
5 # @MAINTAINER:
6 # Michał Górny <mgorny@gentoo.org>
7 # @SUPPORTED_EAPIS: 4 5 6 7
8 # @BLURB: Eclass for fetching and unpacking git repositories.
9 # @DESCRIPTION:
10 # Third generation eclass for easing maintenance of live ebuilds using
11 # git as remote repository.
12
13 case "${EAPI:-0}" in
14         0|1|2|3)
15                 die "Unsupported EAPI=${EAPI} (obsolete) for ${ECLASS}"
16                 ;;
17         4|5|6|7)
18                 ;;
19         *)
20                 die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
21                 ;;
22 esac
23
24 EXPORT_FUNCTIONS src_unpack
25
26 if [[ ! ${_GIT_R3} ]]; then
27
28 if [[ ! ${_INHERITED_BY_GIT_2} ]]; then
29         if [[ ${EAPI:-0} != [0123456] ]]; then
30                 BDEPEND=">=dev-vcs/git-1.8.2.1[curl]"
31         else
32                 DEPEND=">=dev-vcs/git-1.8.2.1[curl]"
33         fi
34 fi
35
36 # @ECLASS-VARIABLE: EGIT_CLONE_TYPE
37 # @DESCRIPTION:
38 # Type of clone that should be used against the remote repository.
39 # This can be either of: 'mirror', 'single', 'shallow'.
40 #
41 # This is intended to be set by user in make.conf. Ebuilds are supposed
42 # to set EGIT_MIN_CLONE_TYPE if necessary instead.
43 #
44 # The 'mirror' type clones all remote branches and tags with complete
45 # history and all notes. EGIT_COMMIT can specify any commit hash.
46 # Upstream-removed branches and tags are purged from the local clone
47 # while fetching. This mode is suitable for cloning the local copy
48 # for development or hosting a local git mirror. However, clones
49 # of repositories with large diverged branches may quickly grow large.
50 #
51 # The 'single+tags' type clones the requested branch and all tags
52 # in the repository. All notes are fetched as well. EGIT_COMMIT
53 # can safely specify hashes throughout the current branch and all tags.
54 # No purging of old references is done (if you often switch branches,
55 # you may need to remove stale branches yourself). This mode is intended
56 # mostly for use with broken git servers such as Google Code that fail
57 # to fetch tags along with the branch in 'single' mode.
58 #
59 # The 'single' type clones only the requested branch or tag. Tags
60 # referencing commits throughout the branch history are fetched as well,
61 # and all notes. EGIT_COMMIT can safely specify only hashes
62 # in the current branch. No purging of old references is done (if you
63 # often switch branches, you may need to remove stale branches
64 # yourself). This mode is suitable for general use.
65 #
66 # The 'shallow' type clones only the newest commit on requested branch
67 # or tag. EGIT_COMMIT can only specify tags, and since the history is
68 # unavailable calls like 'git describe' will not reference prior tags.
69 # No purging of old references is done. This mode is intended mostly for
70 # embedded systems with limited disk space.
71 : ${EGIT_CLONE_TYPE:=single}
72
73 # @ECLASS-VARIABLE: EGIT_MIN_CLONE_TYPE
74 # @DESCRIPTION:
75 # 'Minimum' clone type supported by the ebuild. Takes same values
76 # as EGIT_CLONE_TYPE. When user sets a type that's 'lower' (that is,
77 # later on the list) than EGIT_MIN_CLONE_TYPE, the eclass uses
78 # EGIT_MIN_CLONE_TYPE instead.
79 #
80 # This variable is intended to be used by ebuilds only. Users are
81 # supposed to set EGIT_CLONE_TYPE instead.
82 #
83 # A common case is to use 'single' whenever the build system requires
84 # access to full branch history, or 'single+tags' when Google Code
85 # or a similar remote is used that does not support shallow clones
86 # and fetching tags along with commits. Please use sparingly, and to fix
87 # fatal errors rather than 'non-pretty versions'.
88 : ${EGIT_MIN_CLONE_TYPE:=shallow}
89
90 # @ECLASS-VARIABLE: EGIT3_STORE_DIR
91 # @DESCRIPTION:
92 # Storage directory for git sources.
93 #
94 # This is intended to be set by user in make.conf. Ebuilds must not set
95 # it.
96 #
97 # EGIT3_STORE_DIR=${DISTDIR}/git3-src
98
99 # @ECLASS-VARIABLE: EGIT_MIRROR_URI
100 # @DEFAULT_UNSET
101 # @DESCRIPTION:
102 # 'Top' URI to a local git mirror. If specified, the eclass will try
103 # to fetch from the local mirror instead of using the remote repository.
104 #
105 # The mirror needs to follow EGIT3_STORE_DIR structure. The directory
106 # created by eclass can be used for that purpose.
107 #
108 # Example:
109 # @CODE
110 # EGIT_MIRROR_URI="git://mirror.lan/"
111 # @CODE
112
113 # @ECLASS-VARIABLE: EGIT_REPO_URI
114 # @REQUIRED
115 # @DESCRIPTION:
116 # URIs to the repository, e.g. https://foo. If multiple URIs are
117 # provided, the eclass will consider the remaining URIs as fallbacks
118 # to try if the first URI does not work. For supported URI syntaxes,
119 # read the manpage for git-clone(1).
120 #
121 # URIs should be using https:// whenever possible. http:// and git://
122 # URIs are completely unsecured and their use (even if only as
123 # a fallback) renders the ebuild completely vulnerable to MITM attacks.
124 #
125 # Can be a whitespace-separated list or an array.
126 #
127 # Example:
128 # @CODE
129 # EGIT_REPO_URI="https://a/b.git https://c/d.git"
130 # @CODE
131
132 # @ECLASS-VARIABLE: EVCS_OFFLINE
133 # @DEFAULT_UNSET
134 # @DESCRIPTION:
135 # If non-empty, this variable prevents any online operations.
136
137 # @ECLASS-VARIABLE: EVCS_UMASK
138 # @DEFAULT_UNSET
139 # @DESCRIPTION:
140 # Set this variable to a custom umask. This is intended to be set by
141 # users. By setting this to something like 002, it can make life easier
142 # for people who do development as non-root (but are in the portage
143 # group), and then switch over to building with FEATURES=userpriv.
144 # Or vice-versa. Shouldn't be a security issue here as anyone who has
145 # portage group write access already can screw the system over in more
146 # creative ways.
147
148 # @ECLASS-VARIABLE: EGIT_BRANCH
149 # @DEFAULT_UNSET
150 # @DESCRIPTION:
151 # The branch name to check out. If unset, the upstream default (HEAD)
152 # will be used.
153
154 # @ECLASS-VARIABLE: EGIT_COMMIT
155 # @DEFAULT_UNSET
156 # @DESCRIPTION:
157 # The tag name or commit identifier to check out. If unset, newest
158 # commit from the branch will be used. Note that if set to a commit
159 # not on HEAD branch, EGIT_BRANCH needs to be set to a branch on which
160 # the commit is available.
161
162 # @ECLASS-VARIABLE: EGIT_COMMIT_DATE
163 # @DEFAULT_UNSET
164 # @DESCRIPTION:
165 # Attempt to check out the repository state for the specified timestamp.
166 # The date should be in format understood by 'git rev-list'. The commits
167 # on EGIT_BRANCH will be considered.
168 #
169 # The eclass will select the last commit with commit date preceding
170 # the specified date. When merge commits are found, only first parents
171 # will be considered in order to avoid switching into external branches
172 # (assuming that merges are done correctly). In other words, each merge
173 # will be considered alike a single commit with date corresponding
174 # to the merge commit date.
175
176 # @ECLASS-VARIABLE: EGIT_CHECKOUT_DIR
177 # @DESCRIPTION:
178 # The directory to check the git sources out to.
179 #
180 # EGIT_CHECKOUT_DIR=${WORKDIR}/${P}
181
182 # @ECLASS-VARIABLE: EGIT_SUBMODULES
183 # @DEFAULT_UNSET
184 # @DESCRIPTION:
185 # An array of inclusive and exclusive wildcards on submodule names,
186 # stating which submodules are fetched and checked out. Exclusions
187 # start with '-', and exclude previously matched submodules.
188 #
189 # If unset, all submodules are enabled. Empty list disables all
190 # submodules. In order to use an exclude-only list, start the array
191 # with '*'.
192 #
193 # Remember that wildcards need to be quoted in order to prevent filename
194 # expansion.
195 #
196 # Examples:
197 # @CODE
198 # # Disable all submodules
199 # EGIT_SUBMODULES=()
200 #
201 # # Include only foo and bar
202 # EGIT_SUBMODULES=( foo bar )
203 #
204 # # Use all submodules except for test-* but include test-lib
205 # EGIT_SUBMODULES=( '*' '-test-*' test-lib )
206 # @CODE
207
208 # @FUNCTION: _git-r3_env_setup
209 # @INTERNAL
210 # @DESCRIPTION:
211 # Set the eclass variables as necessary for operation. This can involve
212 # setting EGIT_* to defaults or ${PN}_LIVE_* variables.
213 _git-r3_env_setup() {
214         debug-print-function ${FUNCNAME} "$@"
215
216         # check the clone type
217         case "${EGIT_CLONE_TYPE}" in
218                 mirror|single+tags|single|shallow)
219                         ;;
220                 *)
221                         die "Invalid EGIT_CLONE_TYPE=${EGIT_CLONE_TYPE}"
222         esac
223         case "${EGIT_MIN_CLONE_TYPE}" in
224                 shallow)
225                         ;;
226                 single)
227                         if [[ ${EGIT_CLONE_TYPE} == shallow ]]; then
228                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle\e[22m' mode, adjusting"
229                                 EGIT_CLONE_TYPE=single
230                         fi
231                         ;;
232                 single+tags)
233                         if [[ ${EGIT_CLONE_TYPE} == shallow || ${EGIT_CLONE_TYPE} == single ]]; then
234                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle+tags\e[22m' mode, adjusting"
235                                 EGIT_CLONE_TYPE=single+tags
236                         fi
237                         ;;
238                 mirror)
239                         if [[ ${EGIT_CLONE_TYPE} != mirror ]]; then
240                                 einfo "git-r3: ebuild needs to be cloned in '\e[1mmirror\e[22m' mode, adjusting"
241                                 EGIT_CLONE_TYPE=mirror
242                         fi
243                         ;;
244                 *)
245                         die "Invalid EGIT_MIN_CLONE_TYPE=${EGIT_MIN_CLONE_TYPE}"
246         esac
247
248         if [[ ${EGIT_SUBMODULES[@]+1} && $(declare -p EGIT_SUBMODULES) != "declare -a"* ]]
249         then
250                 die 'EGIT_SUBMODULES must be an array.'
251         fi
252
253         local esc_pn livevar
254         esc_pn=${PN//[-+]/_}
255         [[ ${esc_pn} == [0-9]* ]] && esc_pn=_${esc_pn}
256
257         # note: deprecated, use EGIT_OVERRIDE_* instead
258         livevar=${esc_pn}_LIVE_REPO
259         EGIT_REPO_URI=${!livevar-${EGIT_REPO_URI}}
260         [[ ${!livevar} ]] \
261                 && ewarn "Using ${livevar}, no support will be provided"
262
263         livevar=${esc_pn}_LIVE_BRANCH
264         EGIT_BRANCH=${!livevar-${EGIT_BRANCH}}
265         [[ ${!livevar} ]] \
266                 && ewarn "Using ${livevar}, no support will be provided"
267
268         livevar=${esc_pn}_LIVE_COMMIT
269         EGIT_COMMIT=${!livevar-${EGIT_COMMIT}}
270         [[ ${!livevar} ]] \
271                 && ewarn "Using ${livevar}, no support will be provided"
272
273         livevar=${esc_pn}_LIVE_COMMIT_DATE
274         EGIT_COMMIT_DATE=${!livevar-${EGIT_COMMIT_DATE}}
275         [[ ${!livevar} ]] \
276                 && ewarn "Using ${livevar}, no support will be provided"
277
278         if [[ ${EGIT_COMMIT} && ${EGIT_COMMIT_DATE} ]]; then
279                 die "EGIT_COMMIT and EGIT_COMMIT_DATE can not be specified simultaneously"
280         fi
281
282         # Migration helpers. Remove them when git-2 is removed.
283
284         if [[ ${EGIT_SOURCEDIR} ]]; then
285                 eerror "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR. While updating"
286                 eerror "your ebuild, please check whether the variable is necessary at all"
287                 eerror "since the default has been changed from \${S} to \${WORKDIR}/\${P}."
288                 eerror "Therefore, proper setting of S may be sufficient."
289                 die "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR."
290         fi
291
292         if [[ ${EGIT_MASTER} ]]; then
293                 eerror "EGIT_MASTER has been removed. Instead, the upstream default (HEAD)"
294                 eerror "is used by the eclass. Please remove the assignment or use EGIT_BRANCH"
295                 eerror "as necessary."
296                 die "EGIT_MASTER has been removed."
297         fi
298
299         if [[ ${EGIT_HAS_SUBMODULES} ]]; then
300                 eerror "EGIT_HAS_SUBMODULES has been removed. The eclass no longer needs"
301                 eerror "to switch the clone type in order to support submodules and therefore"
302                 eerror "submodules are detected and fetched automatically. If you need to"
303                 eerror "disable or filter submodules, see EGIT_SUBMODULES."
304                 die "EGIT_HAS_SUBMODULES is no longer necessary."
305         fi
306
307         if [[ ${EGIT_PROJECT} ]]; then
308                 eerror "EGIT_PROJECT has been removed. Instead, the eclass determines"
309                 eerror "the local clone path using path in canonical EGIT_REPO_URI."
310                 eerror "If the current algorithm causes issues for you, please report a bug."
311                 die "EGIT_PROJECT is no longer necessary."
312         fi
313
314         if [[ ${EGIT_BOOTSTRAP} ]]; then
315                 eerror "EGIT_BOOTSTRAP has been removed. Please create proper src_prepare()"
316                 eerror "instead."
317                 die "EGIT_BOOTSTRAP has been removed."
318         fi
319
320         if [[ ${EGIT_NOUNPACK} ]]; then
321                 eerror "EGIT_NOUNPACK has been removed. The eclass no longer calls default"
322                 eerror "unpack function. If necessary, please declare proper src_unpack()."
323                 die "EGIT_NOUNPACK has been removed."
324         fi
325 }
326
327 # @FUNCTION: _git-r3_set_gitdir
328 # @USAGE: <repo-uri>
329 # @INTERNAL
330 # @DESCRIPTION:
331 # Obtain the local repository path and set it as GIT_DIR. Creates
332 # a new repository if necessary.
333 #
334 # <repo-uri> may be used to compose the path. It should therefore be
335 # a canonical URI to the repository.
336 _git-r3_set_gitdir() {
337         debug-print-function ${FUNCNAME} "$@"
338
339         local repo_name=${1#*://*/}
340
341         # strip the trailing slash
342         repo_name=${repo_name%/}
343
344         # strip common prefixes to make paths more likely to match
345         # e.g. git://X/Y.git vs https://X/git/Y.git
346         # (but just one of the prefixes)
347         case "${repo_name}" in
348                 # gnome.org... who else?
349                 browse/*) repo_name=${repo_name#browse/};;
350                 # cgit can proxy requests to git
351                 cgit/*) repo_name=${repo_name#cgit/};;
352                 # pretty common
353                 git/*) repo_name=${repo_name#git/};;
354                 # gentoo.org
355                 gitroot/*) repo_name=${repo_name#gitroot/};;
356                 # sourceforge
357                 p/*) repo_name=${repo_name#p/};;
358                 # kernel.org
359                 pub/scm/*) repo_name=${repo_name#pub/scm/};;
360         esac
361         # ensure a .git suffix, same reason
362         repo_name=${repo_name%.git}.git
363         # now replace all the slashes
364         repo_name=${repo_name//\//_}
365
366         local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}
367         : ${EGIT3_STORE_DIR:=${distdir}/git3-src}
368
369         GIT_DIR=${EGIT3_STORE_DIR}/${repo_name}
370
371         if [[ ! -d ${EGIT3_STORE_DIR} && ! ${EVCS_OFFLINE} ]]; then
372                 (
373                         addwrite /
374                         mkdir -p "${EGIT3_STORE_DIR}"
375                 ) || die "Unable to create ${EGIT3_STORE_DIR}"
376         fi
377
378         addwrite "${EGIT3_STORE_DIR}"
379         if [[ ! -d ${GIT_DIR} ]]; then
380                 if [[ ${EVCS_OFFLINE} ]]; then
381                         eerror "A clone of the following repository is required to proceed:"
382                         eerror "  ${1}"
383                         eerror "However, networking activity has been disabled using EVCS_OFFLINE and there"
384                         eerror "is no local clone available."
385                         die "No local clone of ${1}. Unable to proceed with EVCS_OFFLINE."
386                 fi
387
388                 local saved_umask
389                 if [[ ${EVCS_UMASK} ]]; then
390                         saved_umask=$(umask)
391                         umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
392                 fi
393                 mkdir "${GIT_DIR}" || die
394                 git init --bare || die
395                 if [[ ${saved_umask} ]]; then
396                         umask "${saved_umask}" || die
397                 fi
398         fi
399 }
400
401 # @FUNCTION: _git-r3_set_submodules
402 # @USAGE: <file-contents>
403 # @INTERNAL
404 # @DESCRIPTION:
405 # Parse .gitmodules contents passed as <file-contents>
406 # as in "$(cat .gitmodules)"). Composes a 'submodules' array that
407 # contains in order (name, URL, path) for each submodule.
408 _git-r3_set_submodules() {
409         debug-print-function ${FUNCNAME} "$@"
410
411         local data=${1}
412
413         # ( name url path ... )
414         submodules=()
415
416         local l
417         while read l; do
418                 # submodule.<path>.path=<path>
419                 # submodule.<path>.url=<url>
420                 [[ ${l} == submodule.*.url=* ]] || continue
421
422                 l=${l#submodule.}
423                 local subname=${l%%.url=*}
424
425                 # filter out on EGIT_SUBMODULES
426                 if declare -p EGIT_SUBMODULES &>/dev/null; then
427                         local p l_res res=
428                         for p in "${EGIT_SUBMODULES[@]}"; do
429                                 if [[ ${p} == -* ]]; then
430                                         p=${p#-}
431                                         l_res=
432                                 else
433                                         l_res=1
434                                 fi
435
436                                 [[ ${subname} == ${p} ]] && res=${l_res}
437                         done
438
439                         if [[ ! ${res} ]]; then
440                                 einfo "Skipping submodule \e[1m${subname}\e[22m"
441                                 continue
442                         fi
443                 fi
444
445                 # skip modules that have 'update = none', bug #487262.
446                 local upd=$(echo "${data}" | git config -f /dev/fd/0 \
447                         submodule."${subname}".update)
448                 [[ ${upd} == none ]] && continue
449
450                 # https://github.com/git/git/blob/master/refs.c#L31
451                 # we are more restrictive than git itself but that should not
452                 # cause any issues, #572312, #606950
453                 # TODO: check escaped names for collisions
454                 local enc_subname=${subname//[^a-zA-Z0-9-]/_}
455
456                 submodules+=(
457                         "${enc_subname}"
458                         "$(echo "${data}" | git config -f /dev/fd/0 \
459                                 submodule."${subname}".url || die)"
460                         "$(echo "${data}" | git config -f /dev/fd/0 \
461                                 submodule."${subname}".path || die)"
462                 )
463         done < <(echo "${data}" | git config -f /dev/fd/0 -l || die)
464 }
465
466 # @FUNCTION: _git-r3_set_subrepos
467 # @USAGE: <submodule-uri> <parent-repo-uri>...
468 # @INTERNAL
469 # @DESCRIPTION:
470 # Create 'subrepos' array containing absolute (canonical) submodule URIs
471 # for the given <submodule-uri>. If the URI is relative, URIs will be
472 # constructed using all <parent-repo-uri>s. Otherwise, this single URI
473 # will be placed in the array.
474 _git-r3_set_subrepos() {
475         debug-print-function ${FUNCNAME} "$@"
476
477         local suburl=${1}
478         subrepos=( "${@:2}" )
479
480         if [[ ${suburl} == ./* || ${suburl} == ../* ]]; then
481                 # drop all possible trailing slashes for consistency
482                 subrepos=( "${subrepos[@]%%/}" )
483
484                 while true; do
485                         if [[ ${suburl} == ./* ]]; then
486                                 suburl=${suburl:2}
487                         elif [[ ${suburl} == ../* ]]; then
488                                 suburl=${suburl:3}
489
490                                 # XXX: correctness checking
491
492                                 # drop the last path component
493                                 subrepos=( "${subrepos[@]%/*}" )
494                                 # and then the trailing slashes, again
495                                 subrepos=( "${subrepos[@]%%/}" )
496                         else
497                                 break
498                         fi
499                 done
500
501                 # append the preprocessed path to the preprocessed URIs
502                 subrepos=( "${subrepos[@]/%//${suburl}}")
503         else
504                 subrepos=( "${suburl}" )
505         fi
506 }
507
508
509 # @FUNCTION: _git-r3_is_local_repo
510 # @USAGE: <repo-uri>
511 # @INTERNAL
512 # @DESCRIPTION:
513 # Determine whether the given URI specifies a local (on-disk)
514 # repository.
515 _git-r3_is_local_repo() {
516         debug-print-function ${FUNCNAME} "$@"
517
518         local uri=${1}
519
520         [[ ${uri} == file://* || ${uri} == /* ]]
521 }
522
523 # @FUNCTION: git-r3_fetch
524 # @USAGE: [<repo-uri> [<remote-ref> [<local-id> [<commit-date>]]]]
525 # @DESCRIPTION:
526 # Fetch new commits to the local clone of repository.
527 #
528 # <repo-uri> specifies the repository URIs to fetch from, as a space-
529 # -separated list. The first URI will be used as repository group
530 # identifier and therefore must be used consistently. When not
531 # specified, defaults to ${EGIT_REPO_URI}.
532 #
533 # <remote-ref> specifies the remote ref or commit id to fetch.
534 # It is preferred to use 'refs/heads/<branch-name>' for branches
535 # and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD'
536 # for upstream default branch and hexadecimal commit SHA1. Defaults
537 # to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that
538 # is set to a non-null value.
539 #
540 # <local-id> specifies the local branch identifier that will be used to
541 # locally store the fetch result. It should be unique to multiple
542 # fetches within the repository that can be performed at the same time
543 # (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT%/*}.
544 # This default should be fine unless you are fetching multiple trees
545 # from the same repository in the same ebuild.
546 #
547 # <commit-id> requests attempting to use repository state as of specific
548 # date. For more details, see EGIT_COMMIT_DATE.
549 #
550 # The fetch operation will affect the EGIT_STORE only. It will not touch
551 # the working copy, nor export any environment variables.
552 # If the repository contains submodules, they will be fetched
553 # recursively.
554 git-r3_fetch() {
555         debug-print-function ${FUNCNAME} "$@"
556
557         # process repos first since we create repo_name from it
558         local repos
559         if [[ ${1} ]]; then
560                 repos=( ${1} )
561         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
562                 repos=( "${EGIT_REPO_URI[@]}" )
563         else
564                 repos=( ${EGIT_REPO_URI} )
565         fi
566
567         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
568
569         local r
570         for r in "${repos[@]}"; do
571                 if [[ ${r} == git:* || ${r} == http:* ]]; then
572                         ewarn "git-r3: ${r%%:*} protocol is completely unsecure and may render the ebuild"
573                         ewarn "easily susceptible to MITM attacks (even if used only as fallback). Please"
574                         ewarn "use https instead."
575                         ewarn "[URI: ${r}]"
576                 fi
577         done
578
579         local -x GIT_DIR
580         _git-r3_set_gitdir "${repos[0]}"
581
582         einfo "Repository id: ${GIT_DIR##*/}"
583
584         # prepend the local mirror if applicable
585         if [[ ${EGIT_MIRROR_URI} ]]; then
586                 repos=(
587                         "${EGIT_MIRROR_URI%/}/${GIT_DIR##*/}"
588                         "${repos[@]}"
589                 )
590         fi
591
592         # get the default values for the common variables and override them
593         local branch_name=${EGIT_BRANCH}
594         local commit_id=${2:-${EGIT_COMMIT}}
595         local commit_date=${4:-${EGIT_COMMIT_DATE}}
596
597         # support new override API for EAPI 6+
598         if ! has "${EAPI:-0}" 0 1 2 3 4 5; then
599                 # get the name and do some more processing:
600                 # 1) kill .git suffix,
601                 # 2) underscore (remaining) non-variable characters,
602                 # 3) add preceding underscore if it starts with a digit,
603                 # 4) uppercase.
604                 local override_name=${GIT_DIR##*/}
605                 override_name=${override_name%.git}
606                 override_name=${override_name//[^a-zA-Z0-9_]/_}
607                 override_name=${override_name^^}
608
609                 local varmap=(
610                         REPO:repos
611                         BRANCH:branch_name
612                         COMMIT:commit_id
613                         COMMIT_DATE:commit_date
614                 )
615
616                 local localvar livevar live_warn= override_vars=()
617                 for localvar in "${varmap[@]}"; do
618                         livevar=EGIT_OVERRIDE_${localvar%:*}_${override_name}
619                         localvar=${localvar#*:}
620                         override_vars+=( "${livevar}" )
621
622                         if [[ -n ${!livevar} ]]; then
623                                 [[ ${localvar} == repos ]] && repos=()
624                                 live_warn=1
625                                 ewarn "Using ${livevar}=${!livevar}"
626                                 declare "${localvar}=${!livevar}"
627                         fi
628                 done
629
630                 if [[ ${live_warn} ]]; then
631                         ewarn "No support will be provided."
632                 else
633                         einfo "To override fetched repository properties, use:"
634                         local x
635                         for x in "${override_vars[@]}"; do
636                                 einfo "  ${x}"
637                         done
638                         einfo
639                 fi
640         fi
641
642         # set final variables after applying overrides
643         local branch=${branch_name:+refs/heads/${branch_name}}
644         local remote_ref=${commit_id:-${branch:-HEAD}}
645         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
646         local local_ref=refs/git-r3/${local_id}/__main__
647
648         # try to fetch from the remote
649         local success saved_umask
650         if [[ ${EVCS_UMASK} ]]; then
651                 saved_umask=$(umask)
652                 umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
653         fi
654         for r in "${repos[@]}"; do
655                 if [[ ! ${EVCS_OFFLINE} ]]; then
656                         einfo "Fetching \e[1m${r}\e[22m ..."
657
658                         local fetch_command=( git fetch "${r}" )
659                         local clone_type=${EGIT_CLONE_TYPE}
660
661                         if [[ ${clone_type} == mirror ]]; then
662                                 fetch_command+=(
663                                         --prune
664                                         # mirror the remote branches as local branches
665                                         "+refs/heads/*:refs/heads/*"
666                                         # pull tags explicitly in order to prune them properly
667                                         "+refs/tags/*:refs/tags/*"
668                                         # notes in case something needs them
669                                         "+refs/notes/*:refs/notes/*"
670                                         # and HEAD in case we need the default branch
671                                         # (we keep it in refs/git-r3 since otherwise --prune interferes)
672                                         "+HEAD:refs/git-r3/HEAD"
673                                 )
674                         else # single or shallow
675                                 local fetch_l fetch_r
676
677                                 if [[ ${remote_ref} == HEAD ]]; then
678                                         # HEAD
679                                         fetch_l=HEAD
680                                 elif [[ ${remote_ref} == refs/* ]]; then
681                                         # regular branch, tag or some other explicit ref
682                                         fetch_l=${remote_ref}
683                                 else
684                                         # tag or commit id...
685                                         # let ls-remote figure it out
686                                         local tagref=$(git ls-remote "${r}" "refs/tags/${remote_ref}")
687
688                                         # if it was a tag, ls-remote obtained a hash
689                                         if [[ ${tagref} ]]; then
690                                                 # tag
691                                                 fetch_l=refs/tags/${remote_ref}
692                                         else
693                                                 # commit id
694                                                 # so we need to fetch the whole branch
695                                                 if [[ ${branch} ]]; then
696                                                         fetch_l=${branch}
697                                                 else
698                                                         fetch_l=HEAD
699                                                 fi
700
701                                                 # fetching by commit in shallow mode? can't do.
702                                                 if [[ ${clone_type} == shallow ]]; then
703                                                         clone_type=single
704                                                 fi
705                                         fi
706                                 fi
707
708                                 # checkout by date does not make sense in shallow mode
709                                 if [[ ${commit_date} && ${clone_type} == shallow ]]; then
710                                         clone_type=single
711                                 fi
712
713                                 if [[ ${fetch_l} == HEAD ]]; then
714                                         fetch_r=refs/git-r3/HEAD
715                                 else
716                                         fetch_r=${fetch_l}
717                                 fi
718
719                                 fetch_command+=(
720                                         "+${fetch_l}:${fetch_r}"
721                                 )
722
723                                 if [[ ${clone_type} == single+tags ]]; then
724                                         fetch_command+=(
725                                                 # pull tags explicitly as requested
726                                                 "+refs/tags/*:refs/tags/*"
727                                         )
728                                 fi
729                         fi
730
731                         if [[ ${clone_type} == shallow ]]; then
732                                 if _git-r3_is_local_repo; then
733                                         # '--depth 1' causes sandbox violations with local repos
734                                         # bug #491260
735                                         clone_type=single
736                                 elif [[ ! $(git rev-parse --quiet --verify "${fetch_r}") ]]
737                                 then
738                                         # use '--depth 1' when fetching a new branch
739                                         fetch_command+=( --depth 1 )
740                                 fi
741                         else # non-shallow mode
742                                 if [[ -f ${GIT_DIR}/shallow ]]; then
743                                         fetch_command+=( --unshallow )
744                                 fi
745                         fi
746
747                         set -- "${fetch_command[@]}"
748                         echo "${@}" >&2
749                         "${@}" || continue
750
751                         if [[ ${clone_type} == mirror || ${fetch_l} == HEAD ]]; then
752                                 # update our HEAD to match our remote HEAD ref
753                                 git symbolic-ref HEAD refs/git-r3/HEAD \
754                                                 || die "Unable to update HEAD"
755                         fi
756                 fi
757
758                 # now let's see what the user wants from us
759                 if [[ ${commit_date} ]]; then
760                         local dated_commit_id=$(
761                                 git rev-list --first-parent --before="${commit_date}" \
762                                         -n 1 "${remote_ref}"
763                         )
764                         if [[ ${?} -ne 0 ]]; then
765                                 die "Listing ${remote_ref} failed (wrong ref?)."
766                         elif [[ ! ${dated_commit_id} ]]; then
767                                 die "Unable to find commit for date ${commit_date}."
768                         else
769                                 set -- git update-ref --no-deref "${local_ref}" "${dated_commit_id}"
770                         fi
771                 else
772                         local full_remote_ref=$(
773                                 git rev-parse --verify --symbolic-full-name "${remote_ref}"
774                         )
775
776                         if [[ ${full_remote_ref} ]]; then
777                                 # when we are given a ref, create a symbolic ref
778                                 # so that we preserve the actual argument
779                                 set -- git symbolic-ref "${local_ref}" "${full_remote_ref}"
780                         else
781                                 # otherwise, we were likely given a commit id
782                                 set -- git update-ref --no-deref "${local_ref}" "${remote_ref}"
783                         fi
784                 fi
785
786                 echo "${@}" >&2
787                 if ! "${@}"; then
788                         if [[ ${EVCS_OFFLINE} ]]; then
789                                 eerror "A clone of the following repository is required to proceed:"
790                                 eerror "  ${r}"
791                                 eerror "However, networking activity has been disabled using EVCS_OFFLINE and the local"
792                                 eerror "clone does not have requested ref:"
793                                 eerror "  ${remote_ref}"
794                                 die "Local clone of ${r} does not have requested ref: ${remote_ref}. Unable to proceed with EVCS_OFFLINE."
795                         else
796                                 die "Referencing ${remote_ref} failed (wrong ref?)."
797                         fi
798                 fi
799
800                 success=1
801                 break
802         done
803         if [[ ${saved_umask} ]]; then
804                 umask "${saved_umask}" || die
805         fi
806         [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI"
807
808         # submodules can reference commits in any branch
809         # always use the 'mirror' mode to accomodate that, bug #503332
810         local EGIT_CLONE_TYPE=mirror
811
812         # recursively fetch submodules
813         if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then
814                 local submodules
815                 _git-r3_set_submodules \
816                         "$(git cat-file -p "${local_ref}":.gitmodules || die)"
817
818                 while [[ ${submodules[@]} ]]; do
819                         local subname=${submodules[0]}
820                         local url=${submodules[1]}
821                         local path=${submodules[2]}
822
823                         # use only submodules for which path does exist
824                         # (this is in par with 'git submodule'), bug #551100
825                         # note: git cat-file does not work for submodules
826                         if [[ $(git ls-tree -d "${local_ref}" "${path}") ]]
827                         then
828                                 local commit=$(git rev-parse "${local_ref}:${path}" || die)
829
830                                 if [[ ! ${commit} ]]; then
831                                         die "Unable to get commit id for submodule ${subname}"
832                                 fi
833
834                                 local subrepos
835                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
836
837                                 git-r3_fetch "${subrepos[*]}" "${commit}" "${local_id}/${subname}"
838                         fi
839
840                         submodules=( "${submodules[@]:3}" ) # shift
841                 done
842         fi
843 }
844
845 # @FUNCTION: git-r3_checkout
846 # @USAGE: [<repo-uri> [<checkout-path> [<local-id> [<checkout-paths>...]]]]
847 # @DESCRIPTION:
848 # Check the previously fetched tree to the working copy.
849 #
850 # <repo-uri> specifies the repository URIs, as a space-separated list.
851 # The first URI will be used as repository group identifier
852 # and therefore must be used consistently with git-r3_fetch.
853 # The remaining URIs are not used and therefore may be omitted.
854 # When not specified, defaults to ${EGIT_REPO_URI}.
855 #
856 # <checkout-path> specifies the path to place the checkout. It defaults
857 # to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}.
858 #
859 # <local-id> needs to specify the local identifier that was used
860 # for respective git-r3_fetch.
861 #
862 # If <checkout-paths> are specified, then the specified paths are passed
863 # to 'git checkout' to effect a partial checkout. Please note that such
864 # checkout will not cause the repository to switch branches,
865 # and submodules will be skipped at the moment. The submodules matching
866 # those paths might be checked out in a future version of the eclass.
867 #
868 # The checkout operation will write to the working copy, and export
869 # the repository state into the environment. If the repository contains
870 # submodules, they will be checked out recursively.
871 git-r3_checkout() {
872         debug-print-function ${FUNCNAME} "$@"
873
874         local repos
875         if [[ ${1} ]]; then
876                 repos=( ${1} )
877         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
878                 repos=( "${EGIT_REPO_URI[@]}" )
879         else
880                 repos=( ${EGIT_REPO_URI} )
881         fi
882
883         local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}}
884         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
885         local checkout_paths=( "${@:4}" )
886
887         local -x GIT_DIR
888         _git-r3_set_gitdir "${repos[0]}"
889
890         einfo "Checking out \e[1m${repos[0]}\e[22m to \e[1m${out_dir}\e[22m ..."
891
892         if ! git cat-file -e refs/git-r3/"${local_id}"/__main__; then
893                 die "Logic error: no local clone of ${repos[0]}. git-r3_fetch not used?"
894         fi
895         local remote_ref=$(
896                 git symbolic-ref --quiet refs/git-r3/"${local_id}"/__main__
897         )
898         local new_commit_id=$(
899                 git rev-parse --verify refs/git-r3/"${local_id}"/__main__
900         )
901
902         git-r3_sub_checkout() {
903                 local orig_repo=${GIT_DIR}
904                 local -x GIT_DIR=${out_dir}/.git
905                 local -x GIT_WORK_TREE=${out_dir}
906
907                 mkdir -p "${out_dir}" || die
908
909                 # use git init+fetch instead of clone since the latter doesn't like
910                 # non-empty directories.
911
912                 git init --quiet || die
913                 # setup 'alternates' to avoid copying objects
914                 echo "${orig_repo}/objects" > "${GIT_DIR}"/objects/info/alternates || die
915                 # now copy the refs
916                 cp -R "${orig_repo}"/refs/* "${GIT_DIR}"/refs/ || die
917                 if [[ -f ${orig_repo}/packed-refs ]]; then
918                         cp "${orig_repo}"/packed-refs "${GIT_DIR}"/packed-refs || die
919                 fi
920
921                 # (no need to copy HEAD, we will set it via checkout)
922
923                 if [[ -f ${orig_repo}/shallow ]]; then
924                         cp "${orig_repo}"/shallow "${GIT_DIR}"/ || die
925                 fi
926
927                 set -- git checkout --quiet
928                 if [[ ${remote_ref} ]]; then
929                         set -- "${@}" "${remote_ref#refs/heads/}"
930                 else
931                         set -- "${@}" "${new_commit_id}"
932                 fi
933                 if [[ ${checkout_paths[@]} ]]; then
934                         set -- "${@}" -- "${checkout_paths[@]}"
935                 fi
936                 echo "${@}" >&2
937                 "${@}" || die "git checkout ${remote_ref:-${new_commit_id}} failed"
938         }
939         git-r3_sub_checkout
940         unset -f git-r3_sub_checkout
941
942         local old_commit_id=$(
943                 git rev-parse --quiet --verify refs/git-r3/"${local_id}"/__old__
944         )
945         if [[ ! ${old_commit_id} ]]; then
946                 echo "GIT NEW branch -->"
947                 echo "   repository:               ${repos[0]}"
948                 echo "   at the commit:            ${new_commit_id}"
949         else
950                 # diff against previous revision
951                 echo "GIT update -->"
952                 echo "   repository:               ${repos[0]}"
953                 # write out message based on the revisions
954                 if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then
955                         echo "   updating from commit:     ${old_commit_id}"
956                         echo "   to commit:                ${new_commit_id}"
957
958                         set -- git --no-pager diff --stat \
959                                 ${old_commit_id}..${new_commit_id}
960                         if [[ ${checkout_paths[@]} ]]; then
961                                 set -- "${@}" -- "${checkout_paths[@]}"
962                         fi
963                         "${@}"
964                 else
965                         echo "   at the commit:            ${new_commit_id}"
966                 fi
967         fi
968         git update-ref --no-deref refs/git-r3/"${local_id}"/{__old__,__main__} || die
969
970         # recursively checkout submodules
971         if [[ -f ${out_dir}/.gitmodules && ! ${checkout_paths} ]]; then
972                 local submodules
973                 _git-r3_set_submodules \
974                         "$(<"${out_dir}"/.gitmodules)"
975
976                 while [[ ${submodules[@]} ]]; do
977                         local subname=${submodules[0]}
978                         local url=${submodules[1]}
979                         local path=${submodules[2]}
980
981                         # use only submodules for which path does exist
982                         # (this is in par with 'git submodule'), bug #551100
983                         if [[ -d ${out_dir}/${path} ]]; then
984                                 local subrepos
985                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
986
987                                 git-r3_checkout "${subrepos[*]}" "${out_dir}/${path}" \
988                                         "${local_id}/${subname}"
989                         fi
990
991                         submodules=( "${submodules[@]:3}" ) # shift
992                 done
993         fi
994
995         # keep this *after* submodules
996         export EGIT_DIR=${GIT_DIR}
997         export EGIT_VERSION=${new_commit_id}
998 }
999
1000 # @FUNCTION: git-r3_peek_remote_ref
1001 # @USAGE: [<repo-uri> [<remote-ref>]]
1002 # @DESCRIPTION:
1003 # Peek the reference in the remote repository and print the matching
1004 # (newest) commit SHA1.
1005 #
1006 # <repo-uri> specifies the repository URIs to fetch from, as a space-
1007 # -separated list. When not specified, defaults to ${EGIT_REPO_URI}.
1008 #
1009 # <remote-ref> specifies the remote ref to peek.  It is preferred to use
1010 # 'refs/heads/<branch-name>' for branches and 'refs/tags/<tag-name>'
1011 # for tags. Alternatively, 'HEAD' may be used for upstream default
1012 # branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal
1013 # 'HEAD' that is set to a non-null value.
1014 #
1015 # The operation will be done purely on the remote, without using local
1016 # storage. If commit SHA1 is provided as <remote-ref>, the function will
1017 # fail due to limitations of git protocol.
1018 #
1019 # On success, the function returns 0 and writes hexadecimal commit SHA1
1020 # to stdout. On failure, the function returns 1.
1021 git-r3_peek_remote_ref() {
1022         debug-print-function ${FUNCNAME} "$@"
1023
1024         local repos
1025         if [[ ${1} ]]; then
1026                 repos=( ${1} )
1027         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
1028                 repos=( "${EGIT_REPO_URI[@]}" )
1029         else
1030                 repos=( ${EGIT_REPO_URI} )
1031         fi
1032
1033         local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
1034         local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
1035
1036         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
1037
1038         local r success
1039         for r in "${repos[@]}"; do
1040                 einfo "Peeking \e[1m${remote_ref}\e[22m on \e[1m${r}\e[22m ..." >&2
1041
1042                 local lookup_ref
1043                 if [[ ${remote_ref} == refs/* || ${remote_ref} == HEAD ]]
1044                 then
1045                         lookup_ref=${remote_ref}
1046                 else
1047                         # ls-remote by commit is going to fail anyway,
1048                         # so we may as well pass refs/tags/ABCDEF...
1049                         lookup_ref=refs/tags/${remote_ref}
1050                 fi
1051
1052                 # split on whitespace
1053                 local ref=(
1054                         $(git ls-remote "${r}" "${lookup_ref}")
1055                 )
1056
1057                 if [[ ${ref[0]} ]]; then
1058                         echo "${ref[0]}"
1059                         return 0
1060                 fi
1061         done
1062
1063         return 1
1064 }
1065
1066 git-r3_src_fetch() {
1067         debug-print-function ${FUNCNAME} "$@"
1068
1069         if [[ ! ${EGIT3_STORE_DIR} && ${EGIT_STORE_DIR} ]]; then
1070                 ewarn "You have set EGIT_STORE_DIR but not EGIT3_STORE_DIR. Please consider"
1071                 ewarn "setting EGIT3_STORE_DIR for git-r3.eclass. It is recommended to use"
1072                 ewarn "a different directory than EGIT_STORE_DIR to ease removing old clones"
1073                 ewarn "when git-2 eclass becomes deprecated."
1074         fi
1075
1076         _git-r3_env_setup
1077         git-r3_fetch
1078 }
1079
1080 git-r3_src_unpack() {
1081         debug-print-function ${FUNCNAME} "$@"
1082
1083         _git-r3_env_setup
1084         git-r3_src_fetch
1085         git-r3_checkout
1086 }
1087
1088 # https://bugs.gentoo.org/show_bug.cgi?id=482666
1089 git-r3_pkg_needrebuild() {
1090         debug-print-function ${FUNCNAME} "$@"
1091
1092         local new_commit_id=$(git-r3_peek_remote_ref)
1093         [[ ${new_commit_id} && ${EGIT_VERSION} ]] || die "Lookup failed"
1094
1095         if [[ ${EGIT_VERSION} != ${new_commit_id} ]]; then
1096                 einfo "Update from \e[1m${EGIT_VERSION}\e[22m to \e[1m${new_commit_id}\e[22m"
1097         else
1098                 einfo "Local and remote at \e[1m${EGIT_VERSION}\e[22m"
1099         fi
1100
1101         [[ ${EGIT_VERSION} != ${new_commit_id} ]]
1102 }
1103
1104 # 'export' locally until this gets into EAPI
1105 pkg_needrebuild() { git-r3_pkg_needrebuild; }
1106
1107 _GIT_R3=1
1108 fi