Gentoo Archives: gentoo-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-dev@l.g.o
Cc: "Michał Górny" <mgorny@g.o>
Subject: [gentoo-dev] [PATCH git-r3 06/10] Support single-branch mode.
Date: Wed, 26 Feb 2014 12:02:44
Message-Id: 1393415954-19313-6-git-send-email-mgorny@gentoo.org
In Reply to: [gentoo-dev] [PATCHES git-r3] Clean up and different clone type support by "Michał Górny"
1 This is pretty similar to what git-2.eclass did by default. Instead of
2 fetching all the branches, we only fetch what user told us to. Good news
3 is that it's sometimes a space-saver. Bad news is that it's harder to
4 deal with.
5
6 One of the problems is that EGIT_COMMIT can either specify a tag
7 or a hash (possibly an abbreviated one). And while we can fetch tags
8 (since they are refs), git can't fetch an arbitrary commit by hash. Plus
9 there's the special HEAD case to care about.
10
11 So first, we need to figure out what we're going to fetch actually. If
12 it's a commit hash, we need to fetch the (supposedly) requested branch
13 instead and hope the commit is in it. And if we're fetching HEAD, we
14 need to use the special ref as explained before.
15
16 Then, we need to update 'HEAD' properly. We could supposedly try to
17 figure out what HEAD is before fetching but since we support fetch
18 failures and repository fallback, it's easier to do so after checking
19 that the repo is up.
20
21 It's pretty much the opposite we do in 'mirror' mode. We fetch HEAD into
22 dedicated ref, then use 'git ls-remote' to figure out what branch we
23 actually fetched. Then we move the commits into proper branch and update
24 HEAD.
25 ---
26 eclass/git-r3.eclass | 118 ++++++++++++++++++++++++++++++++++++++++-----------
27 1 file changed, 93 insertions(+), 25 deletions(-)
28
29 diff --git a/eclass/git-r3.eclass b/eclass/git-r3.eclass
30 index 9c8508a..8b7d75d 100644
31 --- a/eclass/git-r3.eclass
32 +++ b/eclass/git-r3.eclass
33 @@ -35,7 +35,7 @@ fi
34 # @ECLASS-VARIABLE: EGIT_CLONE_TYPE
35 # @DESCRIPTION:
36 # Type of clone that should be used against the remote repository.
37 -# This can be either of: 'mirror'.
38 +# This can be either of: 'mirror', 'single'.
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 @@ -43,7 +43,14 @@ fi
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 -: ${EGIT_CLONE_TYPE:=mirror}
47 +#
48 +# The 'single' type clones only the requested branch or tag. Tags
49 +# referencing commits throughout the branch history are fetched as well,
50 +# and all notes. EGIT_COMMIT can safely specify only hashes
51 +# in the current branch. No purging of old references is done (if you
52 +# often switch branches, you may need to remove stale branches
53 +# yourself). This mode is suitable for general use.
54 +: ${EGIT_CLONE_TYPE:=single}
55
56 # @ECLASS-VARIABLE: EGIT3_STORE_DIR
57 # @DESCRIPTION:
58 @@ -122,7 +129,7 @@ _git-r3_env_setup() {
59
60 # check the clone type
61 case "${EGIT_CLONE_TYPE}" in
62 - mirror)
63 + mirror|single)
64 ;;
65 *)
66 die "Invalid EGIT_CLONE_TYPE=${EGIT_CLONE_TYPE}"
67 @@ -305,14 +312,14 @@ _git-r3_is_local_repo() {
68 [[ ${uri} == file://* || ${uri} == /* ]]
69 }
70
71 -# @FUNCTION: _git-r3_update_head
72 -# @USAGE: <remote-head-ref>
73 +# @FUNCTION: _git-r3_find_head
74 +# @USAGE: <head-ref>
75 # @INTERNAL
76 # @DESCRIPTION:
77 -# Given a ref to which remote HEAD was fetched, try to match
78 -# a local branch and update symbolic HEAD appropriately.
79 -_git-r3_update_head()
80 -{
81 +# Given a ref to which remote HEAD was fetched, try to find
82 +# a branch matching the commit. Expects 'git show-ref'
83 +# or 'git ls-remote' output on stdin.
84 +_git-r3_find_head() {
85 debug-print-function ${FUNCNAME} "$@"
86
87 local head_ref=${1}
88 @@ -332,13 +339,13 @@ _git-r3_update_head()
89 matching_ref=${ref}
90 fi
91 fi
92 - done < <(git show-ref --heads || die)
93 + done
94
95 if [[ ! ${matching_ref} ]]; then
96 die "Unable to find a matching branch for remote HEAD (${head_hash})"
97 fi
98
99 - git symbolic-ref HEAD "${matching_ref}" || die
100 + echo "${matching_ref}"
101 }
102
103 # @FUNCTION: git-r3_fetch
104 @@ -406,24 +413,85 @@ git-r3_fetch() {
105 for r in "${repos[@]}"; do
106 einfo "Fetching ${r} ..."
107
108 - local fetch_command=(
109 - git fetch --prune "${r}"
110 - # mirror the remote branches as local branches
111 - "refs/heads/*:refs/heads/*"
112 - # pull tags explicitly in order to prune them properly
113 - "refs/tags/*:refs/tags/*"
114 - # notes in case something needs them
115 - "refs/notes/*:refs/notes/*"
116 - # and HEAD in case we need the default branch
117 - # (we keep it in refs/git-r3 since otherwise --prune interferes)
118 - HEAD:refs/git-r3/HEAD
119 - )
120 + local fetch_command=( git fetch "${r}" )
121 +
122 + if [[ ${EGIT_CLONE_TYPE} == mirror ]]; then
123 + fetch_command+=(
124 + --prune
125 + # mirror the remote branches as local branches
126 + "refs/heads/*:refs/heads/*"
127 + # pull tags explicitly in order to prune them properly
128 + "refs/tags/*:refs/tags/*"
129 + # notes in case something needs them
130 + "refs/notes/*:refs/notes/*"
131 + # and HEAD in case we need the default branch
132 + # (we keep it in refs/git-r3 since otherwise --prune interferes)
133 + HEAD:refs/git-r3/HEAD
134 + )
135 + else # single
136 + local fetch_l fetch_r
137 +
138 + if [[ ${remote_ref} == HEAD ]]; then
139 + # HEAD
140 + fetch_l=HEAD
141 + elif [[ ${remote_ref} == refs/heads/* ]]; then
142 + # regular branch
143 + fetch_l=${remote_ref}
144 + else
145 + # tag or commit...
146 + # let ls-remote figure it out
147 + local tagref=$(git ls-remote "${r}" "refs/tags/${remote_ref}")
148 +
149 + # if it was a tag, ls-remote obtained a hash
150 + if [[ ${tagref} ]]; then
151 + # tag
152 + fetch_l=refs/tags/${remote_ref}
153 + else
154 + # commit, so we need to fetch the branch
155 + # and guess where it takes us...
156 + if [[ ${branch} ]]; then
157 + fetch_l=${branch}
158 + else
159 + fetch_l=HEAD
160 + fi
161 + fi
162 + fi
163 +
164 + if [[ ${fetch_l} == HEAD ]]; then
165 + fetch_r=refs/git-r3/HEAD
166 + else
167 + fetch_r=${fetch_l}
168 + fi
169 +
170 + fetch_command+=(
171 + "${fetch_l}:${fetch_r}"
172 + )
173 + fi
174
175 set -- "${fetch_command[@]}"
176 echo "${@}" >&2
177 if "${@}"; then
178 - # find remote HEAD and update our HEAD properly
179 - _git-r3_update_head refs/git-r3/HEAD
180 + if [[ ${EGIT_CLONE_TYPE} == mirror ]]; then
181 + # find remote HEAD and update our HEAD properly
182 + git symbolic-ref HEAD \
183 + "$(_git-r3_find_head refs/git-r3/HEAD \
184 + < <(git show-ref --heads || die))" \
185 + || die "Unable to update HEAD"
186 + else # single
187 + if [[ ${fetch_l} == HEAD ]]; then
188 + # find out what branch we fetched as HEAD
189 + local head_branch=$(_git-r3_find_head \
190 + refs/git-r3/HEAD \
191 + < <(git ls-remote --heads "${r}" || die))
192 +
193 + # and move it to its regular place
194 + git update-ref --no-deref "${head_branch}" \
195 + refs/git-r3/HEAD \
196 + || die "Unable to sync HEAD branch ${head_branch}"
197 + git symbolic-ref HEAD "${head_branch}" \
198 + || die "Unable to update HEAD"
199 + fi
200 + fi
201
202 # now let's see what the user wants from us
203 local full_remote_ref=$(
204 --
205 1.8.3.2