
In 18c9cb7524
(builtin/clone: create the refdb with the correct object
format, 2023-12-12), we have changed git-clone(1) so that it delays
creation of the refdb until after it has learned about the remote's
object format. This change was required for the reftable backend, which
encodes the object format into the tables. So if we pre-initialized the
refdb with the default object format, but the remote uses a different
object format than that, then the resulting tables would have encoded
the wrong object format.
This change unfortunately breaks remote helpers which try to access the
repository that is about to be created. Because the refdb has not yet
been initialized at the point where we spawn the remote helper, we also
don't yet have "HEAD" or "refs/". Consequently, any Git commands ran by
the remote helper which try to access the repository would fail because
it cannot be discovered.
This is essentially a chicken-and-egg problem: we cannot initialize the
refdb because we don't know about the object format. But we cannot learn
about the object format because the remote helper may be unable to
access the partially-initialized repository.
Ideally, we would address this issue via capabilities. But the remote
helper protocol is not structured in a way that guarantees that the
capability announcement happens before the remote helper tries to access
the repository.
Instead, fix this issue by partially initializing the refdb up to the
point where it becomes discoverable by Git commands.
Reported-by: Mike Hommey <mh@glandium.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
163 lines
3.2 KiB
Bash
Executable File
163 lines
3.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# Copyright (c) 2012 Felipe Contreras
|
|
|
|
# The first argument can be a url when the fetch/push command was a url
|
|
# instead of a configured remote. In this case, use a generic alias.
|
|
if test "$1" = "testgit::$2"; then
|
|
alias=_
|
|
else
|
|
alias=$1
|
|
fi
|
|
url=$2
|
|
|
|
dir="$GIT_DIR/testgit/$alias"
|
|
|
|
if ! git rev-parse --is-inside-git-dir
|
|
then
|
|
exit 1
|
|
fi
|
|
|
|
h_refspec="refs/heads/*:refs/testgit/$alias/heads/*"
|
|
t_refspec="refs/tags/*:refs/testgit/$alias/tags/*"
|
|
|
|
if test -n "$GIT_REMOTE_TESTGIT_NOREFSPEC"
|
|
then
|
|
h_refspec=""
|
|
t_refspec=""
|
|
fi
|
|
|
|
GIT_DIR="$url/.git"
|
|
export GIT_DIR
|
|
|
|
force=
|
|
|
|
mkdir -p "$dir"
|
|
|
|
if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
|
|
then
|
|
gitmarks="$dir/git.marks"
|
|
testgitmarks="$dir/testgit.marks"
|
|
test -e "$gitmarks" || >"$gitmarks"
|
|
test -e "$testgitmarks" || >"$testgitmarks"
|
|
fi
|
|
|
|
while read line
|
|
do
|
|
case $line in
|
|
capabilities)
|
|
echo 'import'
|
|
echo 'export'
|
|
test -n "$h_refspec" && echo "refspec $h_refspec"
|
|
test -n "$t_refspec" && echo "refspec $t_refspec"
|
|
if test -n "$gitmarks"
|
|
then
|
|
echo "*import-marks $gitmarks"
|
|
echo "*export-marks $gitmarks"
|
|
fi
|
|
test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
|
|
test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
|
|
echo 'option'
|
|
echo 'object-format'
|
|
echo
|
|
;;
|
|
list)
|
|
echo ":object-format $(git rev-parse --show-object-format=storage)"
|
|
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
|
|
head=$(git symbolic-ref HEAD)
|
|
echo "@$head HEAD"
|
|
echo
|
|
;;
|
|
import*)
|
|
# read all import lines
|
|
while true
|
|
do
|
|
ref="${line#* }"
|
|
refs="$refs $ref"
|
|
read line
|
|
test "${line%% *}" != "import" && break
|
|
done
|
|
|
|
if test -n "$gitmarks"
|
|
then
|
|
echo "feature import-marks=$gitmarks"
|
|
echo "feature export-marks=$gitmarks"
|
|
fi
|
|
|
|
if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
|
|
then
|
|
echo "feature done"
|
|
exit 1
|
|
fi
|
|
|
|
echo "feature done"
|
|
git fast-export \
|
|
${h_refspec:+"--refspec=$h_refspec"} \
|
|
${t_refspec:+"--refspec=$t_refspec"} \
|
|
${testgitmarks:+"--import-marks=$testgitmarks"} \
|
|
${testgitmarks:+"--export-marks=$testgitmarks"} \
|
|
$refs
|
|
echo "done"
|
|
;;
|
|
export)
|
|
if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
|
|
then
|
|
# consume input so fast-export doesn't get SIGPIPE;
|
|
# git would also notice that case, but we want
|
|
# to make sure we are exercising the later
|
|
# error checks
|
|
while read line; do
|
|
test "done" = "$line" && break
|
|
done
|
|
exit 1
|
|
fi
|
|
|
|
before=$(git for-each-ref --format=' %(refname) %(objectname) ')
|
|
|
|
git fast-import \
|
|
${force:+--force} \
|
|
${testgitmarks:+"--import-marks=$testgitmarks"} \
|
|
${testgitmarks:+"--export-marks=$testgitmarks"} \
|
|
--quiet
|
|
|
|
# figure out which refs were updated
|
|
git for-each-ref --format='%(refname) %(objectname)' |
|
|
while read ref a
|
|
do
|
|
case "$before" in
|
|
*" $ref $a "*)
|
|
continue ;; # unchanged
|
|
esac
|
|
if test -z "$GIT_REMOTE_TESTGIT_PUSH_ERROR"
|
|
then
|
|
echo "ok $ref"
|
|
else
|
|
echo "error $ref $GIT_REMOTE_TESTGIT_PUSH_ERROR"
|
|
fi
|
|
done
|
|
|
|
echo
|
|
;;
|
|
option\ *)
|
|
read cmd opt val <<-EOF
|
|
$line
|
|
EOF
|
|
case $opt in
|
|
force)
|
|
test $val = "true" && force="true" || force=
|
|
echo "ok"
|
|
;;
|
|
object-format)
|
|
test $val = "true" && object_format="true" || object_format=
|
|
echo "ok"
|
|
;;
|
|
*)
|
|
echo "unsupported"
|
|
;;
|
|
esac
|
|
;;
|
|
'')
|
|
exit
|
|
;;
|
|
esac
|
|
done
|