Long story short, our HEAD was set by 'git init' and left at the default
'master'. So whenever we hit a repository that used a different default
branch, we either fetched the wrong branch or failed completely.

Using 'git clone' would fix the issue only temporarily since it does not
provide a way to follow changes of the 'HEAD' branch. git has some code
to obtain symbolic 'HEAD' using some of the transports but it seems not
to be part of the command-line API.

So instead, we do pretty much the same as 'git clone' does when it is
unable to get the symbolic HEAD from remote. We find the branch that is
on the same commit as HEAD, preferably 'master'.

As a note: we need to fetch into a custom ref since git does not allow
fetching to 'HEAD' directly. In fact, 'git fetch HEAD:HEAD' only creates
a branch named 'HEAD', and 'git fetch --prune HEAD:HEAD' creates and
removes it ;).
---
 eclass/git-r3.eclass | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/eclass/git-r3.eclass b/eclass/git-r3.eclass
index 892ed07..7c78b66 100644
--- a/eclass/git-r3.eclass
+++ b/eclass/git-r3.eclass
@@ -270,6 +270,42 @@ _git-r3_is_local_repo() {
        [[ ${uri} == file://* || ${uri} == /* ]]
 }
 
+# @FUNCTION: _git-r3_update_head
+# @USAGE: <remote-head-ref>
+# @INTERNAL
+# @DESCRIPTION:
+# Given a ref to which remote HEAD was fetched, try to match
+# a local branch and update symbolic HEAD appropriately.
+_git-r3_update_head()
+{
+       debug-print-function ${FUNCNAME} "$@"
+
+       local head_ref=${1}
+       local head_hash=$(git rev-parse --verify ${1} || die)
+       local matching_ref
+
+       # TODO: some transports support peeking at symbolic remote refs
+       # find a way to use that rather than guessing
+
+       # (based on guess_remote_head() in git-1.9.0/remote.c)
+       local h ref
+       while read h ref; do
+               # look for matching head
+               if [[ ${h} == ${head_hash} ]]; then
+                       # either take the first matching ref, or master if it 
is there
+                       if [[ ! ${matching_ref} || ${ref} == refs/heads/master 
]]; then
+                               matching_ref=${ref}
+                       fi
+               fi
+       done < <(git show-ref --heads || die)
+
+       if [[ ! ${matching_ref} ]]; then
+               die "Unable to find a matching branch for remote HEAD 
(${head_hash})"
+       fi
+
+       git symbolic-ref HEAD "${matching_ref}" || die
+}
+
 # @FUNCTION: git-r3_fetch
 # @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]]
 # @DESCRIPTION:
@@ -335,11 +371,17 @@ git-r3_fetch() {
                        "refs/tags/*:refs/tags/*"
                        # notes in case something needs them
                        "refs/notes/*:refs/notes/*"
+                       # and HEAD in case we need the default branch
+                       # (we keep it in refs/git-r3 since otherwise --prune 
interferes)
+                       HEAD:refs/git-r3/HEAD
                )
 
                set -- "${fetch_command[@]}"
                echo "${@}" >&2
                if "${@}"; then
+                       # find remote HEAD and update our HEAD properly
+                       _git-r3_update_head refs/git-r3/HEAD
+
                        # now let's see what the user wants from us
                        local full_remote_ref=$(
                                git rev-parse --verify --symbolic-full-name 
"${remote_ref}"
-- 
1.8.3.2


Reply via email to