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