git-fetch: auto-following tags.
[git] / git-fetch.sh
1 #!/bin/sh
2 #
3
4 USAGE='<fetch-options> <repository> <refspec>...'
5 . git-sh-setup
6 . git-parse-remote
7 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
8 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
9
10 LF='
11 '
12 IFS="$LF"
13
14 no_tags=
15 tags=
16 append=
17 force=
18 verbose=
19 update_head_ok=
20 while case "$#" in 0) break ;; esac
21 do
22         case "$1" in
23         -a|--a|--ap|--app|--appe|--appen|--append)
24                 append=t
25                 ;;
26         -f|--f|--fo|--for|--forc|--force)
27                 force=t
28                 ;;
29         -t|--t|--ta|--tag|--tags)
30                 tags=t
31                 ;;
32         -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags)
33                 no_tags=t
34                 ;;
35         -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\
36         --update-he|--update-hea|--update-head|--update-head-|\
37         --update-head-o|--update-head-ok)
38                 update_head_ok=t
39                 ;;
40         -v|--verbose)
41                 verbose=Yes
42                 ;;
43         -*)
44                 usage
45                 ;;
46         *)
47                 break
48                 ;;
49         esac
50         shift
51 done
52
53 case "$#" in
54 0)
55         test -f "$GIT_DIR/branches/origin" ||
56                 test -f "$GIT_DIR/remotes/origin" ||
57                         die "Where do you want to fetch from today?"
58         set origin ;;
59 esac
60
61 remote_nick="$1"
62 remote=$(get_remote_url "$@")
63 refs=
64 rref=
65 rsync_slurped_objects=
66
67 if test "" = "$append"
68 then
69         : >"$GIT_DIR/FETCH_HEAD"
70 fi
71
72 append_fetch_head () {
73     head_="$1"
74     remote_="$2"
75     remote_name_="$3"
76     remote_nick_="$4"
77     local_name_="$5"
78     case "$6" in
79     t) not_for_merge_='not-for-merge' ;;
80     '') not_for_merge_= ;;
81     esac
82
83     # remote-nick is the URL given on the command line (or a shorthand)
84     # remote-name is the $GIT_DIR relative refs/ path we computed
85     # for this refspec.
86     case "$remote_name_" in
87     HEAD)
88         note_= ;;
89     refs/heads/*)
90         note_="$(expr "$remote_name_" : 'refs/heads/\(.*\)')"
91         note_="branch '$note_' of " ;;
92     refs/tags/*)
93         note_="$(expr "$remote_name_" : 'refs/tags/\(.*\)')"
94         note_="tag '$note_' of " ;;
95     *)
96         note_="$remote_name of " ;;
97     esac
98     remote_1_=$(expr "$remote_" : '\(.*\)\.git/*$') &&
99         remote_="$remote_1_"
100     note_="$note_$remote_"
101
102     # 2.6.11-tree tag would not be happy to be fed to resolve.
103     if git-cat-file commit "$head_" >/dev/null 2>&1
104     then
105         headc_=$(git-rev-parse --verify "$head_^0") || exit
106         echo "$headc_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
107         [ "$verbose" ] && echo >&2 "* committish: $head_"
108         [ "$verbose" ] && echo >&2 "  $note_"
109     else
110         echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
111         [ "$verbose" ] && echo >&2 "* non-commit: $head_"
112         [ "$verbose" ] && echo >&2 "  $note_"
113     fi
114     if test "$local_name_" != ""
115     then
116         # We are storing the head locally.  Make sure that it is
117         # a fast forward (aka "reverse push").
118         fast_forward_local "$local_name_" "$head_" "$note_"
119     fi
120 }
121
122 fast_forward_local () {
123     mkdir -p "$(dirname "$GIT_DIR/$1")"
124     case "$1" in
125     refs/tags/*)
126         # Tags need not be pointing at commits so there
127         # is no way to guarantee "fast-forward" anyway.
128         if test -f "$GIT_DIR/$1"
129         then
130                 if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
131                 then
132                         [ "$verbose" ] && echo >&2 "* $1: same as $3"
133                 else
134                         echo >&2 "* $1: updating with $3"
135                 fi
136         else
137                 echo >&2 "* $1: storing $3"
138         fi
139         git-update-ref "$1" "$2" 
140         ;;
141
142     refs/heads/*)
143         # $1 is the ref being updated.
144         # $2 is the new value for the ref.
145         local=$(git-rev-parse --verify "$1^0" 2>/dev/null)
146         if test "$local"
147         then
148             # Require fast-forward.
149             mb=$(git-merge-base "$local" "$2") &&
150             case "$2,$mb" in
151             $local,*)
152                 echo >&2 "* $1: same as $3"
153                 ;;
154             *,$local)
155                 echo >&2 "* $1: fast forward to $3"
156                 git-update-ref "$1" "$2" "$local"
157                 ;;
158             *)
159                 false
160                 ;;
161             esac || {
162                 echo >&2 "* $1: does not fast forward to $3;"
163                 case ",$force,$single_force," in
164                 *,t,*)
165                         echo >&2 "  forcing update."
166                         git-update-ref "$1" "$2" "$local"
167                         ;;
168                 *)
169                         echo >&2 "  not updating."
170                         ;;
171                 esac
172             }
173         else
174             echo >&2 "* $1: storing $3"
175             git-update-ref "$1" "$2"
176         fi
177         ;;
178     esac
179 }
180
181 case "$update_head_ok" in
182 '')
183         orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
184         ;;
185 esac
186
187 # If --tags (and later --heads or --all) is specified, then we are
188 # not talking about defaults stored in Pull: line of remotes or
189 # branches file, and just fetch those and refspecs explicitly given.
190 # Otherwise we do what we always did.
191
192 reflist=$(get_remote_refs_for_fetch "$@")
193 if test "$tags"
194 then
195         taglist=$(IFS=" " &&
196                   git-ls-remote --tags "$remote" |
197                   while read sha1 name
198                   do
199                         case "$name" in
200                         (*^*) continue ;;
201                         esac
202                         if git-check-ref-format "$name"
203                         then
204                             echo ".${name}:${name}"
205                         else
206                             echo >&2 "warning: tag ${name} ignored"
207                         fi
208                   done)
209         if test "$#" -gt 1
210         then
211                 # remote URL plus explicit refspecs; we need to merge them.
212                 reflist="$reflist$LF$taglist"
213         else
214                 # No explicit refspecs; fetch tags only.
215                 reflist=$taglist
216         fi
217 fi
218
219 fetch_main () {
220   reflist="$1"
221   refs=
222
223   for ref in $reflist
224   do
225       refs="$refs$LF$ref"
226
227       # These are relative path from $GIT_DIR, typically starting at refs/
228       # but may be HEAD
229       if expr "$ref" : '\.' >/dev/null
230       then
231           not_for_merge=t
232           ref=$(expr "$ref" : '\.\(.*\)')
233       else
234           not_for_merge=
235       fi
236       if expr "$ref" : '\+' >/dev/null
237       then
238           single_force=t
239           ref=$(expr "$ref" : '\+\(.*\)')
240       else
241           single_force=
242       fi
243       remote_name=$(expr "$ref" : '\([^:]*\):')
244       local_name=$(expr "$ref" : '[^:]*:\(.*\)')
245
246       rref="$rref$LF$remote_name"
247
248       # There are transports that can fetch only one head at a time...
249       case "$remote" in
250       http://* | https://*)
251           if [ -n "$GIT_SSL_NO_VERIFY" ]; then
252               curl_extra_args="-k"
253           fi
254           remote_name_quoted=$(perl -e '
255               my $u = $ARGV[0];
256               $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
257               print "$u";
258           ' "$remote_name")
259           head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
260           expr "$head" : "$_x40\$" >/dev/null ||
261                   die "Failed to fetch $remote_name from $remote"
262           echo >&2 Fetching "$remote_name from $remote" using http
263           git-http-fetch -v -a "$head" "$remote/" || exit
264           ;;
265       rsync://*)
266           TMP_HEAD="$GIT_DIR/TMP_HEAD"
267           rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
268           head=$(git-rev-parse --verify TMP_HEAD)
269           rm -f "$TMP_HEAD"
270           test "$rsync_slurped_objects" || {
271               rsync -av --ignore-existing --exclude info \
272                   "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
273
274               # Look at objects/info/alternates for rsync -- http will
275               # support it natively and git native ones will do it on
276               # the remote end.  Not having that file is not a crime.
277               rsync -q "$remote/objects/info/alternates" \
278                   "$GIT_DIR/TMP_ALT" 2>/dev/null ||
279                   rm -f "$GIT_DIR/TMP_ALT"
280               if test -f "$GIT_DIR/TMP_ALT"
281               then
282                   resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" |
283                   while read alt
284                   do
285                       case "$alt" in 'bad alternate: '*) die "$alt";; esac
286                       echo >&2 "Getting alternate: $alt"
287                       rsync -av --ignore-existing --exclude info \
288                       "$alt" "$GIT_OBJECT_DIRECTORY/" || exit
289                   done
290                   rm -f "$GIT_DIR/TMP_ALT"
291               fi
292               rsync_slurped_objects=t
293           }
294           ;;
295       *)
296           # We will do git native transport with just one call later.
297           continue ;;
298       esac
299
300       append_fetch_head "$head" "$remote" \
301           "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
302
303   done
304
305   case "$remote" in
306   http://* | https://* | rsync://* )
307       ;; # we are already done.
308   *)
309     ( : subshell because we muck with IFS
310       IFS="     $LF"
311       (
312           git-fetch-pack "$remote" $rref || echo failed "$remote"
313       ) |
314       while read sha1 remote_name
315       do
316           case "$sha1" in
317           failed)
318                   echo >&2 "Fetch failure: $remote"
319                   exit 1 ;;
320           esac
321           found=
322           single_force=
323           for ref in $refs
324           do
325               case "$ref" in
326               +$remote_name:*)
327                   single_force=t
328                   not_for_merge=
329                   found="$ref"
330                   break ;;
331               .+$remote_name:*)
332                   single_force=t
333                   not_for_merge=t
334                   found="$ref"
335                   break ;;
336               .$remote_name:*)
337                   not_for_merge=t
338                   found="$ref"
339                   break ;;
340               $remote_name:*)
341                   not_for_merge=
342                   found="$ref"
343                   break ;;
344               esac
345           done
346           local_name=$(expr "$found" : '[^:]*:\(.*\)')
347           append_fetch_head "$sha1" "$remote" \
348                   "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
349       done
350     ) || exit ;;
351   esac
352
353 }
354
355 fetch_main "$reflist"
356
357 # automated tag following
358 case "$no_tags$tags" in
359 '')
360         taglist=$(IFS=" " &&
361         git-ls-remote --tags "$remote" |
362         sed -ne 's|^\([0-9a-f]*\)[      ]\(refs/tags/.*\)^{}$|\1 \2|p' |
363         while read sha1 name
364         do
365                 test -f "$GIT_DIR/$name" && continue
366                 git-check-ref-format "$name" || {
367                         echo >&2 "warning: tag ${name} ignored"
368                         continue
369                 }
370                 git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
371                 echo >&2 "Auto-following $name"
372                 echo ".${name}:${name}"
373         done)
374         case "$taglist" in
375         '') ;;
376         ?*)
377                 fetch_main "$taglist" ;;
378         esac
379 esac
380
381 # If the original head was empty (i.e. no "master" yet), or
382 # if we were told not to worry, we do not have to check.
383 case ",$update_head_ok,$orig_head," in
384 *,, | t,* )
385         ;;
386 *)
387         curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
388         if test "$curr_head" != "$orig_head"
389         then
390                 git-update-ref HEAD "$orig_head"
391                 die "Cannot fetch into the current branch."
392         fi
393         ;;
394 esac