Revision 8e27391a5fdc9194c4ed3ed6c64ec4750a1a08b5 authored by Jonathan Tan on 28 February 2017, 02:53:11 UTC, committed by Junio C Hamano on 28 February 2017, 19:35:53 UTC
http.c supports HTTP redirects of the form

  http://foo/info/refs?service=git-upload-pack
  -> http://anything
  -> http://bar/info/refs?service=git-upload-pack

(that is to say, as long as the Git part of the path and the query
string is preserved in the final redirect destination, the intermediate
steps can have any URL). However, if one of the intermediate steps
results in an HTTP exception, a confusing "unable to update url base
from redirection" message is printed instead of a Curl error message
with the HTTP exception code.

This was introduced by 2 commits. Commit c93c92f ("http: update base
URLs when we see redirects", 2013-09-28) introduced a best-effort
optimization that required checking if only the "base" part of the URL
differed between the initial request and the final redirect destination,
but it performed the check before any HTTP status checking was done. If
something went wrong, the normal code path was still followed, so this
did not cause any confusing error messages until commit 6628eb4 ("http:
always update the base URL for redirects", 2016-12-06), which taught
http to die if the non-"base" part of the URL differed.

Therefore, teach http to check the HTTP status before attempting to
check if only the "base" part of the URL differed. This commit teaches
http_request_reauth to return early without updating options->base_url
upon an error; the only invoker of this function that passes a non-NULL
"options" is remote-curl.c (through "http_get_strbuf"), which only uses
options->base_url for an informational message in the situations that
this commit cares about (that is, when the return value is not HTTP_OK).

The included test checks that the redirect scheme at the beginning of
this commit message works, and that returning a 502 in the middle of the
redirect scheme produces the correct result. Note that this is different
from the test in commit 6628eb4 ("http: always update the base URL for
redirects", 2016-12-06) in that this commit tests that a Git-shaped URL
(http://.../info/refs?service=git-upload-pack) works, whereas commit
6628eb4 tests that a non-Git-shaped URL
(http://.../info/refs/foo?service=git-upload-pack) does not work (even
though Git is processing that URL) and is an error that is fatal, not
silently swallowed.

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Acked-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent e7e07d5
Raw File
git-request-pull.sh
#!/bin/sh
# Copyright 2005, Ryan Anderson <ryan@michonline.com>
#
# This file is licensed under the GPL v2, or a later version
# at the discretion of Linus Torvalds.

SUBDIRECTORY_OK='Yes'
OPTIONS_KEEPDASHDASH=
OPTIONS_STUCKLONG=
OPTIONS_SPEC='git request-pull [options] start url [end]
--
p    show patch text as well
'

. git-sh-setup

GIT_PAGER=
export GIT_PAGER

patch=
while	case "$#" in 0) break ;; esac
do
	case "$1" in
	-p)
		patch=-p ;;
	--)
		shift; break ;;
	-*)
		usage ;;
	*)
		break ;;
	esac
	shift
done

base=$1 url=$2 status=0

test -n "$base" && test -n "$url" || usage

baserev=$(git rev-parse --verify --quiet "$base"^0)
if test -z "$baserev"
then
    die "fatal: Not a valid revision: $base"
fi

#
# $3 must be a symbolic ref, a unique ref, or
# a SHA object expression. It can also be of
# the format 'local-name:remote-name'.
#
local=${3%:*}
local=${local:-HEAD}
remote=${3#*:}
pretty_remote=${remote#refs/}
pretty_remote=${pretty_remote#heads/}
head=$(git symbolic-ref -q "$local")
head=${head:-$(git show-ref --heads --tags "$local" | cut -d' ' -f2)}
head=${head:-$(git rev-parse --quiet --verify "$local")}

# None of the above? Bad.
test -z "$head" && die "fatal: Not a valid revision: $local"

# This also verifies that the resulting head is unique:
# "git show-ref" could have shown multiple matching refs..
headrev=$(git rev-parse --verify --quiet "$head"^0)
test -z "$headrev" && die "fatal: Ambiguous revision: $local"

# Was it a branch with a description?
branch_name=${head#refs/heads/}
if test "z$branch_name" = "z$headref" ||
	! git config "branch.$branch_name.description" >/dev/null
then
	branch_name=
fi

merge_base=$(git merge-base $baserev $headrev) ||
die "fatal: No commits in common between $base and $head"

# $head is the refname from the command line.
# If a ref with the same name as $head exists at the remote
# and their values match, use that.
#
# Otherwise find a random ref that matches $headrev.
find_matching_ref='
	my ($head,$headrev) = (@ARGV);
	my ($found);

	while (<STDIN>) {
		chomp;
		my ($sha1, $ref, $deref) = /^(\S+)\s+([^^]+)(\S*)$/;
		my ($pattern);
		next unless ($sha1 eq $headrev);

		$pattern="/$head\$";
		if ($ref eq $head) {
			$found = $ref;
		}
		if ($ref =~ /$pattern/) {
			$found = $ref;
		}
		if ($sha1 eq $head) {
			$found = $sha1;
		}
	}
	if ($found) {
		print "$found\n";
	}
'

ref=$(git ls-remote "$url" | @@PERL@@ -e "$find_matching_ref" "${remote:-HEAD}" "$headrev")

if test -z "$ref"
then
	echo "warn: No match for commit $headrev found at $url" >&2
	echo "warn: Are you sure you pushed '${remote:-HEAD}' there?" >&2
	status=1
fi

# Special case: turn "for_linus" to "tags/for_linus" when it is correct
if test "$ref" = "refs/tags/$pretty_remote"
then
	pretty_remote=tags/$pretty_remote
fi

url=$(git ls-remote --get-url "$url")

git show -s --format='The following changes since commit %H:

  %s (%ci)

are available in the git repository at:
' $merge_base &&
echo "  $url $pretty_remote" &&
git show -s --format='
for you to fetch changes up to %H:

  %s (%ci)

----------------------------------------------------------------' $headrev &&

if test $(git cat-file -t "$head") = tag
then
	git cat-file tag "$head" |
	sed -n -e '1,/^$/d' -e '/^-----BEGIN PGP /q' -e p
	echo
	echo "----------------------------------------------------------------"
fi &&

if test -n "$branch_name"
then
	echo "(from the branch description for $branch_name local branch)"
	echo
	git config "branch.$branch_name.description"
	echo "----------------------------------------------------------------"
fi &&

git shortlog ^$baserev $headrev &&
git diff -M --stat --summary $patch $merge_base..$headrev || status=1

exit $status
back to top