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
git-mergetool.sh
#!/bin/sh
#
# This program resolves merge conflicts in git
#
# Copyright (c) 2006 Theodore Y. Ts'o
# Copyright (c) 2009-2016 David Aguilar
#
# This file is licensed under the GPL v2, or a later version
# at the discretion of Junio C Hamano.
#
USAGE='[--tool=tool] [--tool-help] [-y|--no-prompt|--prompt] [-O<orderfile>] [file to merge] ...'
SUBDIRECTORY_OK=Yes
NONGIT_OK=Yes
OPTIONS_SPEC=
TOOL_MODE=merge
. git-sh-setup
. git-mergetool--lib
# Returns true if the mode reflects a symlink
is_symlink () {
test "$1" = 120000
}
is_submodule () {
test "$1" = 160000
}
local_present () {
test -n "$local_mode"
}
remote_present () {
test -n "$remote_mode"
}
base_present () {
test -n "$base_mode"
}
mergetool_tmpdir_init () {
if test "$(git config --bool mergetool.writeToTemp)" != true
then
MERGETOOL_TMPDIR=.
return 0
fi
if MERGETOOL_TMPDIR=$(mktemp -d -t "git-mergetool-XXXXXX" 2>/dev/null)
then
return 0
fi
die "error: mktemp is needed when 'mergetool.writeToTemp' is true"
}
cleanup_temp_files () {
if test "$1" = --save-backup
then
rm -rf -- "$MERGED.orig"
test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
rm -f -- "$LOCAL" "$REMOTE" "$BASE"
else
rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
fi
if test "$MERGETOOL_TMPDIR" != "."
then
rmdir "$MERGETOOL_TMPDIR"
fi
}
describe_file () {
mode="$1"
branch="$2"
file="$3"
printf " {%s}: " "$branch"
if test -z "$mode"
then
echo "deleted"
elif is_symlink "$mode"
then
echo "a symbolic link -> '$(cat "$file")'"
elif is_submodule "$mode"
then
echo "submodule commit $file"
elif base_present
then
echo "modified file"
else
echo "created file"
fi
}
resolve_symlink_merge () {
while true
do
printf "Use (l)ocal or (r)emote, or (a)bort? "
read ans || return 1
case "$ans" in
[lL]*)
git checkout-index -f --stage=2 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
return 0
;;
[rR]*)
git checkout-index -f --stage=3 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
return 0
;;
[aA]*)
return 1
;;
esac
done
}
resolve_deleted_merge () {
while true
do
if base_present
then
printf "Use (m)odified or (d)eleted file, or (a)bort? "
else
printf "Use (c)reated or (d)eleted file, or (a)bort? "
fi
read ans || return 1
case "$ans" in
[mMcC]*)
git add -- "$MERGED"
if test "$merge_keep_backup" = "true"
then
cleanup_temp_files --save-backup
else
cleanup_temp_files
fi
return 0
;;
[dD]*)
git rm -- "$MERGED" > /dev/null
cleanup_temp_files
return 0
;;
[aA]*)
if test "$merge_keep_temporaries" = "false"
then
cleanup_temp_files
fi
return 1
;;
esac
done
}
resolve_submodule_merge () {
while true
do
printf "Use (l)ocal or (r)emote, or (a)bort? "
read ans || return 1
case "$ans" in
[lL]*)
if ! local_present
then
if test -n "$(git ls-tree HEAD -- "$MERGED")"
then
# Local isn't present, but it's a subdirectory
git ls-tree --full-name -r HEAD -- "$MERGED" |
git update-index --index-info || exit $?
else
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
git update-index --force-remove "$MERGED"
cleanup_temp_files --save-backup
fi
elif is_submodule "$local_mode"
then
stage_submodule "$MERGED" "$local_sha1"
else
git checkout-index -f --stage=2 -- "$MERGED"
git add -- "$MERGED"
fi
return 0
;;
[rR]*)
if ! remote_present
then
if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
then
# Remote isn't present, but it's a subdirectory
git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" |
git update-index --index-info || exit $?
else
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
git update-index --force-remove "$MERGED"
fi
elif is_submodule "$remote_mode"
then
! is_submodule "$local_mode" &&
test -e "$MERGED" &&
mv -- "$MERGED" "$BACKUP"
stage_submodule "$MERGED" "$remote_sha1"
else
test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
git checkout-index -f --stage=3 -- "$MERGED"
git add -- "$MERGED"
fi
cleanup_temp_files --save-backup
return 0
;;
[aA]*)
return 1
;;
esac
done
}
stage_submodule () {
path="$1"
submodule_sha1="$2"
mkdir -p "$path" ||
die "fatal: unable to create directory for module at $path"
# Find $path relative to work tree
work_tree_root=$(cd_to_toplevel && pwd)
work_rel_path=$(cd "$path" &&
GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix
)
test -n "$work_rel_path" ||
die "fatal: unable to get path of module $path relative to work tree"
git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
}
checkout_staged_file () {
tmpfile=$(expr \
"$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
: '\([^ ]*\) ')
if test $? -eq 0 && test -n "$tmpfile"
then
mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
else
>"$3"
fi
}
merge_file () {
MERGED="$1"
f=$(git ls-files -u -- "$MERGED")
if test -z "$f"
then
if test ! -f "$MERGED"
then
echo "$MERGED: file not found"
else
echo "$MERGED: file does not need merging"
fi
return 1
fi
if BASE=$(expr "$MERGED" : '\(.*\)\.[^/]*$')
then
ext=$(expr "$MERGED" : '.*\(\.[^/]*\)$')
else
BASE=$MERGED
ext=
fi
mergetool_tmpdir_init
if test "$MERGETOOL_TMPDIR" != "."
then
# If we're using a temporary directory then write to the
# top-level of that directory.
BASE=${BASE##*/}
fi
BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
if is_submodule "$local_mode" || is_submodule "$remote_mode"
then
echo "Submodule merge conflict for '$MERGED':"
local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
describe_file "$local_mode" "local" "$local_sha1"
describe_file "$remote_mode" "remote" "$remote_sha1"
resolve_submodule_merge
return
fi
if test -f "$MERGED"
then
mv -- "$MERGED" "$BACKUP"
cp -- "$BACKUP" "$MERGED"
fi
# Create a parent directory to handle delete/delete conflicts
# where the base's directory no longer exists.
mkdir -p "$(dirname "$MERGED")"
checkout_staged_file 1 "$MERGED" "$BASE"
checkout_staged_file 2 "$MERGED" "$LOCAL"
checkout_staged_file 3 "$MERGED" "$REMOTE"
if test -z "$local_mode" || test -z "$remote_mode"
then
echo "Deleted merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
status=$?
rmdir -p "$(dirname "$MERGED")" 2>/dev/null
return $status
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"
then
echo "Symbolic link merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
echo "Normal merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
if test "$guessed_merge_tool" = true || test "$prompt" = true
then
printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
read ans || return 1
fi
if base_present
then
present=true
else
present=false
fi
if ! run_merge_tool "$merge_tool" "$present"
then
echo "merge of $MERGED failed" 1>&2
mv -- "$BACKUP" "$MERGED"
if test "$merge_keep_temporaries" = "false"
then
cleanup_temp_files
fi
return 1
fi
if test "$merge_keep_backup" = "true"
then
mv -- "$BACKUP" "$MERGED.orig"
else
rm -- "$BACKUP"
fi
git add -- "$MERGED"
cleanup_temp_files
return 0
}
prompt_after_failed_merge () {
while true
do
printf "Continue merging other unresolved paths [y/n]? "
read ans || return 1
case "$ans" in
[yY]*)
return 0
;;
[nN]*)
return 1
;;
esac
done
}
print_noop_and_exit () {
echo "No files need merging"
exit 0
}
main () {
prompt=$(git config --bool mergetool.prompt)
guessed_merge_tool=false
orderfile=
while test $# != 0
do
case "$1" in
--tool-help=*)
TOOL_MODE=${1#--tool-help=}
show_tool_help
;;
--tool-help)
show_tool_help
;;
-t|--tool*)
case "$#,$1" in
*,*=*)
merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
;;
1,*)
usage ;;
*)
merge_tool="$2"
shift ;;
esac
;;
-y|--no-prompt)
prompt=false
;;
--prompt)
prompt=true
;;
-O*)
orderfile="${1#-O}"
;;
--)
shift
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
git_dir_init
require_work_tree
if test -z "$merge_tool"
then
# Check if a merge tool has been configured
merge_tool=$(get_configured_merge_tool)
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"
then
merge_tool=$(guess_merge_tool) || exit
guessed_merge_tool=true
fi
fi
merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
prefix=$(git rev-parse --show-prefix) || exit 1
cd_to_toplevel
if test -n "$orderfile"
then
orderfile=$(
git rev-parse --prefix "$prefix" -- "$orderfile" |
sed -e 1d
)
fi
if test $# -eq 0 && test -e "$GIT_DIR/MERGE_RR"
then
set -- $(git rerere remaining)
if test $# -eq 0
then
print_noop_and_exit
fi
elif test $# -ge 0
then
# rev-parse provides the -- needed for 'set'
eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
fi
files=$(git -c core.quotePath=false \
diff --name-only --diff-filter=U \
${orderfile:+"-O$orderfile"} -- "$@")
if test -z "$files"
then
print_noop_and_exit
fi
printf "Merging:\n"
printf "%s\n" "$files"
rc=0
for i in $files
do
printf "\n"
if ! merge_file "$i"
then
rc=1
prompt_after_failed_merge || exit 1
fi
done
exit $rc
}
main "$@"
Computing file changes ...