And time for a small update. 

Dnia 2013-08-31, o godz. 01:37:44
Michał Górny <mgo...@gentoo.org> napisał(a):

> 3. sys-fs/bedup's btrfs-progs submodule says the given commit id is
> 'not a valid branch point'. Need to investigate what this means.
> 
> 4. 'git fetch --depth 1' seems to be refetching stuff even when nothing
> changed. Need to investigate it. It may be enough to do an additional
> 'did anything change?' check.

Those are fixed now. The eclass has been adjusted to work properly with
branches, tags and commit ids. It also avoids shallowing repo that was
unshallowed already (since we expect that one of the packages needs it
non-shallow), and properly unshallows shallow repos ;).

Attaching the new version and a diff.

-- 
Best regards,
Michał Górny
# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

# @ECLASS: git-r3.eclass
# @MAINTAINER:
# Michał Górny <mgo...@gentoo.org>
# @BLURB: Eclass for fetching and unpacking git repositories.
# @DESCRIPTION:
# Third generation eclass for easing maitenance of live ebuilds using
# git as remote repository. Eclass supports lightweight (shallow)
# clones, local object deduplication and submodules.

case "${EAPI:-0}" in
        0|1|2|3|4|5)
                ;;
        *)
                die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
                ;;
esac

if [[ ! ${_GIT_R3} ]]; then

inherit eutils

fi

EXPORT_FUNCTIONS src_unpack

if [[ ! ${_GIT_R3} ]]; then

# @ECLASS-VARIABLE: EGIT_STORE_DIR
# @DESCRIPTION:
# Storage directory for git sources.
#
# EGIT_STORE_DIR=${DISTDIR}/git3-src

# @ECLASS-VARIABLE: EGIT_REPO_URI
# @REQUIRED
# @DESCRIPTION:
# URIs to the repository, e.g. git://foo, https://foo. If multiple URIs
# are provided, the eclass will consider them as fallback URIs to try
# if the first URI does not work.
#
# It can be overriden via env using ${PN}_LIVE_REPO variable.
#
# Example:
# @CODE
# EGIT_REPO_URI="git://a/b.git https://c/d.git";
# @CODE

# @ECLASS-VARIABLE: EVCS_OFFLINE
# @DEFAULT_UNSET
# @DESCRIPTION:
# If non-empty, this variable prevents any online operations.

# @ECLASS-VARIABLE: EGIT_BRANCH
# @DEFAULT_UNSET
# @DESCRIPTION:
# The branch name to check out. If unset, the upstream default (HEAD)
# will be used.
#
# It can be overriden via env using ${PN}_LIVE_BRANCH variable.

# @ECLASS-VARIABLE: EGIT_COMMIT
# @DEFAULT_UNSET
# @DESCRIPTION:
# The tag name or commit identifier to check out. If unset, newest
# commit from the branch will be used. If set, EGIT_BRANCH will
# be ignored.
#
# It can be overriden via env using ${PN}_LIVE_COMMIT variable.

# @ECLASS-VARIABLE: EGIT_NONSHALLOW
# @DEFAULT_UNSET
# @DESCRIPTION:
# Disable performing shallow fetches/clones. Shallow clones have
# a fair number of limitations. Therefore, if you'd like the eclass to
# perform complete clones instead, set this to a non-null value.
#
# This variable is to be set in make.conf. Ebuilds are not allowed
# to set it.

# @FUNCTION: _git-r3_env_setup
# @INTERNAL
# @DESCRIPTION:
# Set the eclass variables as necessary for operation. This can involve
# setting EGIT_* to defaults or ${PN}_LIVE_* variables.
_git-r3_env_setup() {
        debug-print-function ${FUNCNAME} "$@"

        local esc_pn livevar
        esc_pn=${PN//[-+]/_}

        livevar=${esc_pn}_LIVE_REPO
        EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}}
        [[ ${EGIT_REPO_URI} ]] \
                || die "EGIT_REPO_URI must be set to a non-empty value"
        [[ ${!livevar} ]] \
                && ewarn "Using ${livevar}, no support will be provided"

        livevar=${esc_pn}_LIVE_BRANCH
        EGIT_BRANCH=${!livevar:-${EGIT_BRANCH}}
        [[ ${!livevar} ]] \
                && ewarn "Using ${livevar}, no support will be provided"

        livevar=${esc_pn}_LIVE_COMMIT
        EGIT_COMMIT=${!livevar:-${EGIT_COMMIT}}
        [[ ${!livevar} ]] \
                && ewarn "Using ${livevar}, no support will be provided"

        # git-2 unsupported cruft
        local v
        for v in EGIT_{SOURCEDIR,MASTER,HAS_SUBMODULES,PROJECT} \
                        EGIT_{NOUNPACK,BOOTSTRAP}
        do
                [[ ${!v} ]] && die "${v} is not supported."
        done
}

# @FUNCTION: _git-r3_set_gitdir
# @USAGE: <repo-uri>
# @INTERNAL
# @DESCRIPTION:
# Obtain the local repository path and set it as GIT_DIR. Creates
# a new repository if necessary.
#
# <repo-uri> may be used to compose the path. It should therefore be
# a canonical URI to the repository.
_git-r3_set_gitdir() {
        debug-print-function ${FUNCNAME} "$@"

        local repo_name=${1#*://*/}

        # strip common prefixes to make paths more likely to match
        # e.g. git://X/Y.git vs https://X/git/Y.git
        # (but just one of the prefixes)
        case "${repo_name}" in
                # cgit can proxy requests to git
                cgit/*) repo_name=${repo_name#cgit/};;
                # pretty common
                git/*) repo_name=${repo_name#git/};;
                # gentoo.org
                gitroot/*) repo_name=${repo_name#gitroot/};;
                # google code, sourceforge
                p/*) repo_name=${repo_name#p/};;
                # kernel.org
                pub/scm/*) repo_name=${repo_name#pub/scm/};;
        esac
        # ensure a .git suffix, same reason
        repo_name=${repo_name%.git}.git
        # now replace all the slashes
        repo_name=${repo_name//\//_}

        local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}
        : ${EGIT_STORE_DIR:=${distdir}/git3-src}

        GIT_DIR=${EGIT_STORE_DIR}/${repo_name}

        if [[ ! -d ${EGIT_STORE_DIR} ]]; then
                (
                        addwrite /
                        mkdir -m0755 -p "${EGIT_STORE_DIR}"
                ) || die "Unable to create ${EGIT_STORE_DIR}"
        fi

        addwrite "${EGIT_STORE_DIR}"
        if [[ ! -d ${GIT_DIR} ]]; then
                mkdir "${GIT_DIR}" || die
                git init --bare || die

                # avoid auto-unshallow :)
                touch "${GIT_DIR}"/shallow || die
        fi
}

# @FUNCTION: _git-r3_set_submodules
# @USAGE: <file-contents>
# @INTERNAL
# @DESCRIPTION:
# Parse .gitmodules contents passed as <file-contents>
# as in "$(cat .gitmodules)"). Composes a 'submodules' array that
# contains in order (name, URL, path) for each submodule.
_git-r3_set_submodules() {
        debug-print-function ${FUNCNAME} "$@"

        local data=${1}

        # ( name url path ... )
        submodules=()

        local l
        while read l; do
                # submodule.<path>.path=<path>
                # submodule.<path>.url=<url>
                [[ ${l} == submodule.*.url=* ]] || continue

                l=${l#submodule.}
                local subname=${l%%.url=*}

                submodules+=(
                        "${subname}"
                        "$(echo "${data}" | git config -f /dev/fd/0 \
                                submodule."${subname}".url)"
                        "$(echo "${data}" | git config -f /dev/fd/0 \
                                submodule."${subname}".path)"
                )
        done < <(echo "${data}" | git config -f /dev/fd/0 -l)
}

# @FUNCTION: git-r3_fetch
# @USAGE: <repo-uri> <remote-ref> <local-id>
# @DESCRIPTION:
# Fetch new commits to the local clone of repository. <repo-uri> follows
# the syntax of EGIT_REPO_URI and may list multiple (fallback) URIs.
# <remote-ref> specifies the remote ref to fetch (branch, tag
# or commit). <local-id> specifies an identifier that needs to uniquely
# identify the fetch operation in case multiple parallel merges used
# the git repo. <local-id> usually involves using CATEGORY, PN and SLOT.
#
# The fetch operation will only affect the local storage. It will not
# touch the working copy. If the repository contains submodules, they
# will be fetched recursively as well.
git-r3_fetch() {
        debug-print-function ${FUNCNAME} "$@"

        local repos=( ${1} )
        local remote_ref=${2}
        local local_id=${3}
        local local_ref=refs/heads/${local_id}/__main__

        local -x GIT_DIR
        _git-r3_set_gitdir ${repos[0]}

        # try to fetch from the remote
        local r success
        for r in ${repos[@]}; do
                einfo "Fetching ${remote_ref} from ${r} ..."

                local is_branch lookup_ref
                if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]]
                then
                        is_branch=1
                        lookup_ref=${remote_ref}
                else
                        # ls-remote by commit is going to fail anyway,
                        # so we may as well pass refs/tags/ABCDEF...
                        lookup_ref=refs/tags/${remote_ref}
                fi

                # first, try ls-remote to see if ${remote_ref} is a real ref
                # and not a commit id. if it succeeds, we can pass ${remote_ref}
                # to 'fetch'. otherwise, we will just fetch everything

                # split on whitespace
                local ref=(
                        $(git ls-remote "${r}" "${lookup_ref}")
                )

                # now, another important thing. we may only fetch a remote
                # branch directly to a local branch. Otherwise, we need to fetch
                # the commit and re-create the branch on top of it.

                local ref_param=()
                if [[ ! ${ref[0]} ]]; then
                        local EGIT_NONSHALLOW=1
                fi

                if [[ ! -f ${GIT_DIR}/shallow ]]; then
                        # if it's a complete repo, fetch it as-is
                        :
                elif [[ ${EGIT_NONSHALLOW} ]]; then
                        # if it's a shallow clone but we need complete,
                        # unshallow it
                        ref_param+=( --unshallow )
                else
                        # otherwise, just fetch as shallow
                        ref_param+=( --depth 1 )
                fi

                if [[ ${ref[0]} ]]; then
                        if [[ ${is_branch} ]]; then
                                ref_param+=( -f 
"${remote_ref}:${local_id}/__main__" )
                        else
                                ref_param+=( "${remote_ref}" )
                        fi
                fi

                # if ${remote_ref} is branch or tag, ${ref[@]} will contain
                # the respective commit id. otherwise, it will be an empty
                # array, so the following won't evaluate to a parameter.
                set -- git fetch --no-tags "${r}" "${ref_param[@]}"
                echo "${@}" >&2
                if "${@}"; then
                        if [[ ! ${is_branch} ]]; then
                                set -- git branch -f "${local_id}/__main__" \
                                        "${ref[0]:-${remote_ref}}"
                                echo "${@}" >&2
                                if ! "${@}"; then
                                        die "Creating branch for ${remote_ref} 
failed (wrong ref?)."
                                fi
                        fi

                        success=1
                        break
                fi
        done
        [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI"

        # recursively fetch submodules
        if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then
                local submodules
                _git-r3_set_submodules \
                        "$(git cat-file -p "${local_ref}":.gitmodules || die)"

                while [[ ${submodules[@]} ]]; do
                        local subname=${submodules[0]}
                        local url=${submodules[1]}
                        local path=${submodules[2]}
                        local commit=$(git rev-parse "${local_ref}:${path}")

                        if [[ ! ${commit} ]]; then
                                die "Unable to get commit id for submodule 
${subname}"
                        fi

                        git-r3_fetch "${url}" "${commit}" 
"${local_id}/${subname}"

                        submodules=( "${submodules[@]:3}" ) # shift
                done
        fi
}

# @FUNCTION: git-r3_checkout
# @USAGE: <repo-uri> <local-id> <path>
# @DESCRIPTION:
# Check the previously fetched commit out to <path> (usually
# ${WORKDIR}/${P}). <repo-uri> follows the syntax of EGIT_REPO_URI
# and will be used to re-construct the local storage path. <local-id>
# is the unique identifier used for the fetch operation and will
# be used to obtain the proper commit.
#
# If the repository contains submodules, they will be checked out
# recursively as well.
git-r3_checkout() {
        debug-print-function ${FUNCNAME} "$@"

        local repos=( ${1} )
        local local_id=${2}
        local out_dir=${3}

        local -x GIT_DIR GIT_WORK_TREE
        _git-r3_set_gitdir ${repos[0]}
        GIT_WORK_TREE=${out_dir}

        einfo "Checking out ${repos[0]} to ${out_dir} ..."

        mkdir -p "${GIT_WORK_TREE}"
        set -- git checkout -f "${local_id}"/__main__ .
        echo "${@}" >&2
        "${@}" || die "git checkout ${local_id}/__main__ failed"

        # diff against previous revision (if any)
        local new_commit_id=$(git rev-parse --verify "${local_id}"/__main__)
        local old_commit_id=$(
                git rev-parse --verify "${local_id}"/__old__ 2>/dev/null
        )

        if [[ ! ${old_commit_id} ]]; then
                echo "GIT NEW branch -->"
                echo "   repository:               ${repos[0]}"
                echo "   at the commit:            ${new_commit_id}"
        else
                echo "GIT update -->"
                echo "   repository:               ${repos[0]}"
                # write out message based on the revisions
                if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then
                        echo "   updating from commit:     ${old_commit_id}"
                        echo "   to commit:                ${new_commit_id}"

                        git --no-pager diff --color --stat \
                                ${old_commit_id}..${new_commit_id}
                else
                        echo "   at the commit:            ${new_commit_id}"
                fi
        fi
        git branch -f "${local_id}"/{__old__,__main__} || die

        # recursively checkout submodules
        if [[ -f ${GIT_WORK_TREE}/.gitmodules ]]; then
                local submodules
                _git-r3_set_submodules \
                        "$(cat "${GIT_WORK_TREE}"/.gitmodules)"

                while [[ ${submodules[@]} ]]; do
                        local subname=${submodules[0]}
                        local url=${submodules[1]}
                        local path=${submodules[2]}

                        git-r3_checkout "${url}" "${local_id}/${subname}" \
                                "${GIT_WORK_TREE}/${path}"

                        submodules=( "${submodules[@]:3}" ) # shift
                done
        fi

        # keep this *after* submodules
        export EGIT_DIR=${GIT_DIR}
        export EGIT_VERSION=${new_commit_id}
}

git-r3_src_fetch() {
        debug-print-function ${FUNCNAME} "$@"

        [[ ${EVCS_OFFLINE} ]] && return

        _git-r3_env_setup
        local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
        git-r3_fetch "${EGIT_REPO_URI}" \
                "${EGIT_COMMIT:-${branch:-HEAD}}" \
                ${CATEGORY}/${PN}/${SLOT}
}

git-r3_src_unpack() {
        debug-print-function ${FUNCNAME} "$@"

        _git-r3_env_setup
        git-r3_src_fetch
        git-r3_checkout "${EGIT_REPO_URI}" \
                ${CATEGORY}/${PN}/${SLOT} \
                "${WORKDIR}/${P}"
}

_GIT_R3=1
fi
diff --git a/gx86/eclass/git-r3.eclass b/gx86/eclass/git-r3.eclass
index 36673a8..d56c818 100644
--- a/gx86/eclass/git-r3.eclass
+++ b/gx86/eclass/git-r3.eclass
@@ -168,6 +168,9 @@ _git-r3_set_gitdir() {
 	if [[ ! -d ${GIT_DIR} ]]; then
 		mkdir "${GIT_DIR}" || die
 		git init --bare || die
+
+		# avoid auto-unshallow :)
+		touch "${GIT_DIR}"/shallow || die
 	fi
 }
 
@@ -234,29 +237,70 @@ git-r3_fetch() {
 	for r in ${repos[@]}; do
 		einfo "Fetching ${remote_ref} from ${r} ..."
 
+		local is_branch lookup_ref
+		if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]]
+		then
+			is_branch=1
+			lookup_ref=${remote_ref}
+		else
+			# ls-remote by commit is going to fail anyway,
+			# so we may as well pass refs/tags/ABCDEF...
+			lookup_ref=refs/tags/${remote_ref}
+		fi
+
 		# first, try ls-remote to see if ${remote_ref} is a real ref
 		# and not a commit id. if it succeeds, we can pass ${remote_ref}
 		# to 'fetch'. otherwise, we will just fetch everything
 
 		# split on whitespace
 		local ref=(
-			$(git ls-remote "${r}" "${remote_ref}")
+			$(git ls-remote "${r}" "${lookup_ref}")
 		)
 
+		# now, another important thing. we may only fetch a remote
+		# branch directly to a local branch. Otherwise, we need to fetch
+		# the commit and re-create the branch on top of it.
+
 		local ref_param=()
+		if [[ ! ${ref[0]} ]]; then
+			local EGIT_NONSHALLOW=1
+		fi
+
+		if [[ ! -f ${GIT_DIR}/shallow ]]; then
+			# if it's a complete repo, fetch it as-is
+			:
+		elif [[ ${EGIT_NONSHALLOW} ]]; then
+			# if it's a shallow clone but we need complete,
+			# unshallow it
+			ref_param+=( --unshallow )
+		else
+			# otherwise, just fetch as shallow
+			ref_param+=( --depth 1 )
+		fi
+
 		if [[ ${ref[0]} ]]; then
-			[[ ${EGIT_NONSHALLOW} ]] || ref_param+=( --depth 1 )
-			ref_param+=( "${remote_ref}" )
+			if [[ ${is_branch} ]]; then
+				ref_param+=( -f "${remote_ref}:${local_id}/__main__" )
+			else
+				ref_param+=( "${remote_ref}" )
+			fi
 		fi
 
 		# if ${remote_ref} is branch or tag, ${ref[@]} will contain
 		# the respective commit id. otherwise, it will be an empty
 		# array, so the following won't evaluate to a parameter.
-		if git fetch --no-tags "${r}" "${ref_param[@]}"; then
-			if ! git branch -f "${local_id}/__main__" "${ref[0]:-${remote_ref}}"
-			then
-				die "Creating tag failed (${remote_ref} invalid?)"
+		set -- git fetch --no-tags "${r}" "${ref_param[@]}"
+		echo "${@}" >&2
+		if "${@}"; then
+			if [[ ! ${is_branch} ]]; then
+				set -- git branch -f "${local_id}/__main__" \
+					"${ref[0]:-${remote_ref}}"
+				echo "${@}" >&2
+				if ! "${@}"; then
+					die "Creating branch for ${remote_ref} failed (wrong ref?)."
+				fi
 			fi
+
 			success=1
 			break
 		fi
@@ -311,7 +355,9 @@ git-r3_checkout() {
 	einfo "Checking out ${repos[0]} to ${out_dir} ..."
 
 	mkdir -p "${GIT_WORK_TREE}"
-	git checkout -f "${local_id}"/__main__ || die
+	set -- git checkout -f "${local_id}"/__main__ .
+	echo "${@}" >&2
+	"${@}" || die "git checkout ${local_id}/__main__ failed"
 
 	# diff against previous revision (if any)
 	local new_commit_id=$(git rev-parse --verify "${local_id}"/__main__)
@@ -330,6 +376,9 @@ git-r3_checkout() {
 		if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then
 			echo "   updating from commit:     ${old_commit_id}"
 			echo "   to commit:                ${new_commit_id}"
+
+			git --no-pager diff --color --stat \
+				${old_commit_id}..${new_commit_id}
 		else
 			echo "   at the commit:            ${new_commit_id}"
 		fi

Attachment: signature.asc
Description: PGP signature

Reply via email to