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