git-fetch: auto-following tags.

I added things to ls-remote so that Cogito can auto-follow tags
easily and correctly a while ago, but git-fetch did not use the
facility.  Recently added git-describe command relies on
repository keeping up-to-date set of tags, which made it much
more attractive to automatically follow tags, so we do that as
well.

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-01-07 00:48:04 -08:00
parent 026351a035
commit 03febf99bc
2 changed files with 169 additions and 124 deletions

View File

@ -10,15 +10,23 @@
fetches is a descendant of `<lbranch>`. This option fetches is a descendant of `<lbranch>`. This option
overrides that check. overrides that check.
\--no-tags::
By default, `git-fetch` fetches tags that point at
objects that are downloaded from the remote repository
and stores them locally. This option disables this
automatic tag following.
-t, \--tags:: -t, \--tags::
By default, the git core utilities will not fetch and store Most of the tags are fetched automatically as branch
tags under the same name as the remote repository; ask it heads are downloaded, but tags that do not point at
to do so using `--tags`. Using this option will bound the objects reachable from the branch heads that are being
list of objects pulled to the remote tags. Commits in branches tracked will not be fetched by this mechanism. This
beyond the tags will be ignored. flag lets all tags and their associated objects be
downloaded.
-u, \--update-head-ok:: -u, \--update-head-ok::
By default `git-fetch` refuses to update the head which By default `git-fetch` refuses to update the head which
corresponds to the current branch. This flag disables the corresponds to the current branch. This flag disables the
check. Note that fetching into the current branch will not check. Note that fetching into the current branch will not
update the index and working directory, so use it with care. update the index and working directory, so use it with care.

View File

@ -11,6 +11,7 @@ LF='
' '
IFS="$LF" IFS="$LF"
no_tags=
tags= tags=
append= append=
force= force=
@ -28,6 +29,9 @@ do
-t|--t|--ta|--tag|--tags) -t|--t|--ta|--tag|--tags)
tags=t tags=t
;; ;;
-n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags)
no_tags=t
;;
-u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\
--update-he|--update-hea|--update-head|--update-head-|\ --update-he|--update-hea|--update-head|--update-head-|\
--update-head-o|--update-head-ok) --update-head-o|--update-head-ok)
@ -212,133 +216,166 @@ then
fi fi
fi fi
for ref in $reflist fetch_main () {
do reflist="$1"
refs="$refs$LF$ref" refs=
# These are relative path from $GIT_DIR, typically starting at refs/ for ref in $reflist
# but may be HEAD do
if expr "$ref" : '\.' >/dev/null refs="$refs$LF$ref"
then
not_for_merge=t
ref=$(expr "$ref" : '\.\(.*\)')
else
not_for_merge=
fi
if expr "$ref" : '\+' >/dev/null
then
single_force=t
ref=$(expr "$ref" : '\+\(.*\)')
else
single_force=
fi
remote_name=$(expr "$ref" : '\([^:]*\):')
local_name=$(expr "$ref" : '[^:]*:\(.*\)')
rref="$rref$LF$remote_name" # These are relative path from $GIT_DIR, typically starting at refs/
# but may be HEAD
if expr "$ref" : '\.' >/dev/null
then
not_for_merge=t
ref=$(expr "$ref" : '\.\(.*\)')
else
not_for_merge=
fi
if expr "$ref" : '\+' >/dev/null
then
single_force=t
ref=$(expr "$ref" : '\+\(.*\)')
else
single_force=
fi
remote_name=$(expr "$ref" : '\([^:]*\):')
local_name=$(expr "$ref" : '[^:]*:\(.*\)')
# There are transports that can fetch only one head at a time... rref="$rref$LF$remote_name"
case "$remote" in
http://* | https://*)
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
remote_name_quoted=$(perl -e '
my $u = $ARGV[0];
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
print "$u";
' "$remote_name")
head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
expr "$head" : "$_x40\$" >/dev/null ||
die "Failed to fetch $remote_name from $remote"
echo >&2 Fetching "$remote_name from $remote" using http
git-http-fetch -v -a "$head" "$remote/" || exit
;;
rsync://*)
TMP_HEAD="$GIT_DIR/TMP_HEAD"
rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
head=$(git-rev-parse --verify TMP_HEAD)
rm -f "$TMP_HEAD"
test "$rsync_slurped_objects" || {
rsync -av --ignore-existing --exclude info \
"$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
# Look at objects/info/alternates for rsync -- http will # There are transports that can fetch only one head at a time...
# support it natively and git native ones will do it on the remote case "$remote" in
# end. Not having that file is not a crime. http://* | https://*)
rsync -q "$remote/objects/info/alternates" \ if [ -n "$GIT_SSL_NO_VERIFY" ]; then
"$GIT_DIR/TMP_ALT" 2>/dev/null || curl_extra_args="-k"
rm -f "$GIT_DIR/TMP_ALT" fi
if test -f "$GIT_DIR/TMP_ALT" remote_name_quoted=$(perl -e '
then my $u = $ARGV[0];
resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" | $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
while read alt print "$u";
do ' "$remote_name")
case "$alt" in 'bad alternate: '*) die "$alt";; esac head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
echo >&2 "Getting alternate: $alt" expr "$head" : "$_x40\$" >/dev/null ||
rsync -av --ignore-existing --exclude info \ die "Failed to fetch $remote_name from $remote"
"$alt" "$GIT_OBJECT_DIRECTORY/" || exit echo >&2 Fetching "$remote_name from $remote" using http
done git-http-fetch -v -a "$head" "$remote/" || exit
rm -f "$GIT_DIR/TMP_ALT" ;;
fi rsync://*)
rsync_slurped_objects=t TMP_HEAD="$GIT_DIR/TMP_HEAD"
} rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
;; head=$(git-rev-parse --verify TMP_HEAD)
*) rm -f "$TMP_HEAD"
# We will do git native transport with just one call later. test "$rsync_slurped_objects" || {
continue ;; rsync -av --ignore-existing --exclude info \
esac "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
append_fetch_head "$head" "$remote" \ # Look at objects/info/alternates for rsync -- http will
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge" # support it natively and git native ones will do it on
# the remote end. Not having that file is not a crime.
rsync -q "$remote/objects/info/alternates" \
"$GIT_DIR/TMP_ALT" 2>/dev/null ||
rm -f "$GIT_DIR/TMP_ALT"
if test -f "$GIT_DIR/TMP_ALT"
then
resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" |
while read alt
do
case "$alt" in 'bad alternate: '*) die "$alt";; esac
echo >&2 "Getting alternate: $alt"
rsync -av --ignore-existing --exclude info \
"$alt" "$GIT_OBJECT_DIRECTORY/" || exit
done
rm -f "$GIT_DIR/TMP_ALT"
fi
rsync_slurped_objects=t
}
;;
*)
# We will do git native transport with just one call later.
continue ;;
esac
done append_fetch_head "$head" "$remote" \
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
case "$remote" in done
http://* | https://* | rsync://* )
;; # we are already done. case "$remote" in
*) http://* | https://* | rsync://* )
IFS=" $LF" ;; # we are already done.
( *)
git-fetch-pack "$remote" $rref || echo failed "$remote" ( : subshell because we muck with IFS
) | IFS=" $LF"
while read sha1 remote_name (
do git-fetch-pack "$remote" $rref || echo failed "$remote"
case "$sha1" in ) |
failed) while read sha1 remote_name
echo >&2 "Fetch failure: $remote" do
exit 1 ;; case "$sha1" in
esac failed)
found= echo >&2 "Fetch failure: $remote"
single_force= exit 1 ;;
for ref in $refs esac
found=
single_force=
for ref in $refs
do
case "$ref" in
+$remote_name:*)
single_force=t
not_for_merge=
found="$ref"
break ;;
.+$remote_name:*)
single_force=t
not_for_merge=t
found="$ref"
break ;;
.$remote_name:*)
not_for_merge=t
found="$ref"
break ;;
$remote_name:*)
not_for_merge=
found="$ref"
break ;;
esac
done
local_name=$(expr "$found" : '[^:]*:\(.*\)')
append_fetch_head "$sha1" "$remote" \
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
done
) || exit ;;
esac
}
fetch_main "$reflist"
# automated tag following
case "$no_tags$tags" in
'')
taglist=$(IFS=" " &&
git-ls-remote --tags "$remote" |
sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' |
while read sha1 name
do do
case "$ref" in test -f "$GIT_DIR/$name" && continue
+$remote_name:*) git-check-ref-format "$name" || {
single_force=t echo >&2 "warning: tag ${name} ignored"
not_for_merge= continue
found="$ref" }
break ;; git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
.+$remote_name:*) echo >&2 "Auto-following $name"
single_force=t echo ".${name}:${name}"
not_for_merge=t done)
found="$ref" case "$taglist" in
break ;; '') ;;
.$remote_name:*) ?*)
not_for_merge=t fetch_main "$taglist" ;;
found="$ref" esac
break ;;
$remote_name:*)
not_for_merge=
found="$ref"
break ;;
esac
done
local_name=$(expr "$found" : '[^:]*:\(.*\)')
append_fetch_head "$sha1" "$remote" \
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
done || exit
;;
esac esac
# If the original head was empty (i.e. no "master" yet), or # If the original head was empty (i.e. no "master" yet), or