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 |