2013-12-05 13:02:40 +00:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='fetch/clone from a shallow clone'
|
|
|
|
|
|
|
|
. ./test-lib.sh
|
|
|
|
|
|
|
|
commit() {
|
|
|
|
echo "$1" >tracked &&
|
|
|
|
git add tracked &&
|
|
|
|
git commit -m "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'setup' '
|
|
|
|
commit 1 &&
|
|
|
|
commit 2 &&
|
|
|
|
commit 3 &&
|
|
|
|
commit 4 &&
|
|
|
|
git config --global transfer.fsckObjects true
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'setup shallow clone' '
|
|
|
|
git clone --no-local --depth=2 .git shallow &&
|
|
|
|
git --git-dir=shallow/.git log --format=%s >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'clone from shallow clone' '
|
|
|
|
git clone --no-local shallow shallow2 &&
|
|
|
|
(
|
|
|
|
cd shallow2 &&
|
|
|
|
git fsck &&
|
|
|
|
git log --format=%s >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fetch from shallow clone' '
|
|
|
|
(
|
|
|
|
cd shallow &&
|
|
|
|
commit 5
|
|
|
|
) &&
|
|
|
|
(
|
|
|
|
cd shallow2 &&
|
|
|
|
git fetch &&
|
|
|
|
git fsck &&
|
|
|
|
git log --format=%s origin/master >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
5
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fetch --depth from shallow clone' '
|
|
|
|
(
|
|
|
|
cd shallow &&
|
|
|
|
commit 6
|
|
|
|
) &&
|
|
|
|
(
|
|
|
|
cd shallow2 &&
|
|
|
|
git fetch --depth=2 &&
|
|
|
|
git fsck &&
|
|
|
|
git log --format=%s origin/master >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
6
|
|
|
|
5
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2013-12-05 13:02:41 +00:00
|
|
|
test_expect_success 'fetch --unshallow from shallow clone' '
|
|
|
|
(
|
|
|
|
cd shallow2 &&
|
|
|
|
git fetch --unshallow &&
|
|
|
|
git fsck &&
|
|
|
|
git log --format=%s origin/master >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
6
|
|
|
|
5
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2013-12-05 13:02:40 +00:00
|
|
|
test_expect_success 'fetch something upstream has but hidden by clients shallow boundaries' '
|
|
|
|
# the blob "1" is available in .git but hidden by the
|
|
|
|
# shallow2/.git/shallow and it should be resent
|
2016-01-04 09:10:45 +00:00
|
|
|
! git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null &&
|
2013-12-05 13:02:40 +00:00
|
|
|
echo 1 >1.t &&
|
|
|
|
git add 1.t &&
|
|
|
|
git commit -m add-1-back &&
|
|
|
|
(
|
|
|
|
cd shallow2 &&
|
|
|
|
git fetch ../.git +refs/heads/master:refs/remotes/top/master &&
|
|
|
|
git fsck &&
|
|
|
|
git log --format=%s top/master >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
add-1-back
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
) &&
|
2016-01-04 09:10:45 +00:00
|
|
|
git --git-dir=shallow2/.git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null
|
2013-12-05 13:02:40 +00:00
|
|
|
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fetch that requires changes in .git/shallow is filtered' '
|
|
|
|
(
|
|
|
|
cd shallow &&
|
|
|
|
git checkout --orphan no-shallow &&
|
|
|
|
commit no-shallow
|
|
|
|
) &&
|
|
|
|
git init notshallow &&
|
|
|
|
(
|
|
|
|
cd notshallow &&
|
|
|
|
git fetch ../shallow/.git refs/heads/*:refs/remotes/shallow/*&&
|
|
|
|
git for-each-ref --format="%(refname)" >actual.refs &&
|
|
|
|
cat <<EOF >expect.refs &&
|
|
|
|
refs/remotes/shallow/no-shallow
|
|
|
|
EOF
|
|
|
|
test_cmp expect.refs actual.refs &&
|
|
|
|
git log --format=%s shallow/no-shallow >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
no-shallow
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2013-12-05 13:02:42 +00:00
|
|
|
test_expect_success 'fetch --update-shallow' '
|
|
|
|
(
|
|
|
|
cd shallow &&
|
|
|
|
git checkout master &&
|
|
|
|
commit 7 &&
|
|
|
|
git tag -m foo heavy-tag HEAD^ &&
|
|
|
|
git tag light-tag HEAD^:tracked
|
|
|
|
) &&
|
|
|
|
(
|
|
|
|
cd notshallow &&
|
|
|
|
git fetch --update-shallow ../shallow/.git refs/heads/*:refs/remotes/shallow/* &&
|
|
|
|
git fsck &&
|
|
|
|
git for-each-ref --sort=refname --format="%(refname)" >actual.refs &&
|
|
|
|
cat <<EOF >expect.refs &&
|
|
|
|
refs/remotes/shallow/master
|
|
|
|
refs/remotes/shallow/no-shallow
|
|
|
|
refs/tags/heavy-tag
|
|
|
|
refs/tags/light-tag
|
|
|
|
EOF
|
|
|
|
test_cmp expect.refs actual.refs &&
|
|
|
|
git log --format=%s shallow/master >actual &&
|
|
|
|
cat <<EOF >expect &&
|
|
|
|
7
|
|
|
|
6
|
|
|
|
5
|
|
|
|
4
|
|
|
|
3
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2014-03-11 12:59:46 +00:00
|
|
|
test_expect_success POSIXPERM,SANITY 'shallow fetch from a read-only repo' '
|
|
|
|
cp -R .git read-only.git &&
|
|
|
|
test_when_finished "find read-only.git -type d -print | xargs chmod +w" &&
|
2018-06-15 18:13:39 +00:00
|
|
|
find read-only.git -print | xargs chmod -w &&
|
2014-03-11 12:59:46 +00:00
|
|
|
git clone --no-local --depth=2 read-only.git from-read-only &&
|
|
|
|
git --git-dir=from-read-only/.git log --format=%s >actual &&
|
|
|
|
cat >expect <<EOF &&
|
|
|
|
add-1-back
|
|
|
|
4
|
|
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
|
|
'
|
|
|
|
|
repack -ad: prune the list of shallow commits
`git repack` can drop unreachable commits without further warning,
making the corresponding entries in `.git/shallow` invalid, which causes
serious problems when deepening the branches.
One scenario where unreachable commits are dropped by `git repack` is
when a `git fetch --prune` (or even a `git fetch` when a ref was
force-pushed in the meantime) can make a commit unreachable that was
reachable before.
Therefore it is not safe to assume that a `git repack -adlf` will keep
unreachable commits alone (under the assumption that they had not been
packed in the first place, which is an assumption at least some of Git's
code seems to make).
This is particularly important to keep in mind when looking at the
`.git/shallow` file: if any commits listed in that file become
unreachable, it is not a problem, but if they go missing, it *is* a
problem. One symptom of this problem is that a deepening fetch may now
fail with
fatal: error in object: unshallow <commit-hash>
To avoid this problem, let's prune the shallow list in `git repack` when
the `-d` option is passed, unless `-A` is passed, too (which would force
the now-unreachable objects to be turned into loose objects instead of
being deleted). Additionally, we also need to take `--keep-reachable`
and `--unpack-unreachable=<date>` into account.
Note: an alternative solution discussed during the review of this patch
was to teach `git fetch` to simply ignore entries in .git/shallow if the
corresponding commits do not exist locally. A quick test, however,
revealed that the .git/shallow file is written during a shallow *clone*,
in which case the commits do not exist, either, but the "shallow" line
*does* need to be sent. Therefore, this approach would be a lot more
finicky than the approach presented by the this patch.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-24 15:56:13 +00:00
|
|
|
test_expect_success '.git/shallow is edited by repack' '
|
2018-10-24 15:56:10 +00:00
|
|
|
git init shallow-server &&
|
|
|
|
test_commit -C shallow-server A &&
|
|
|
|
test_commit -C shallow-server B &&
|
|
|
|
git -C shallow-server checkout -b branch &&
|
|
|
|
test_commit -C shallow-server C &&
|
|
|
|
test_commit -C shallow-server E &&
|
|
|
|
test_commit -C shallow-server D &&
|
|
|
|
d="$(git -C shallow-server rev-parse --verify D^0)" &&
|
|
|
|
git -C shallow-server checkout master &&
|
|
|
|
|
|
|
|
git clone --depth=1 --no-tags --no-single-branch \
|
|
|
|
"file://$PWD/shallow-server" shallow-client &&
|
|
|
|
|
|
|
|
: now remove the branch and fetch with prune &&
|
|
|
|
git -C shallow-server branch -D branch &&
|
|
|
|
git -C shallow-client fetch --prune --depth=1 \
|
|
|
|
origin "+refs/heads/*:refs/remotes/origin/*" &&
|
|
|
|
git -C shallow-client repack -adfl &&
|
|
|
|
test_must_fail git -C shallow-client rev-parse --verify $d^0 &&
|
|
|
|
! grep $d shallow-client/.git/shallow &&
|
|
|
|
|
|
|
|
git -C shallow-server branch branch-orig $d &&
|
|
|
|
git -C shallow-client fetch --prune --depth=2 \
|
|
|
|
origin "+refs/heads/*:refs/remotes/origin/*"
|
|
|
|
'
|
|
|
|
|
fetch-pack: write shallow, then check connectivity
When fetching, connectivity is checked after the shallow file is
updated. There are 2 issues with this: (1) the connectivity check is
only performed up to ancestors of existing refs (which is not thorough
enough if we were deepening an existing ref in the first place), and (2)
there is no rollback of the shallow file if the connectivity check
fails.
To solve (1), update the connectivity check to check the ancestry chain
completely in the case of a deepening fetch by refraining from passing
"--not --all" when invoking rev-list in connected.c.
To solve (2), have fetch_pack() perform its own connectivity check
before updating the shallow file. To support existing use cases in which
"git fetch-pack" is used to download objects without much regard as to
the connectivity of the resulting objects with respect to the existing
repository, the connectivity check is only done if necessary (that is,
the fetch is not a clone, and the fetch involves shallow/deepen
functionality). "git fetch" still performs its own connectivity check,
preserving correctness but sometimes performing redundant work. This
redundancy is mitigated by the fact that fetch_pack() reports if it has
performed a connectivity check itself, and if the transport supports
connect or stateless-connect, it will bubble up that report so that "git
fetch" knows not to perform the connectivity check in such a case.
This was noticed when a user tried to deepen an existing repository by
fetching with --no-shallow from a server that did not send all necessary
objects - the connectivity check as run by "git fetch" succeeded, but a
subsequent "git fsck" failed.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-07-02 22:08:43 +00:00
|
|
|
. "$TEST_DIRECTORY"/lib-httpd.sh
|
|
|
|
start_httpd
|
|
|
|
|
|
|
|
REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
|
|
|
|
|
|
|
|
test_expect_success 'shallow fetches check connectivity before writing shallow file' '
|
|
|
|
rm -rf "$REPO" client &&
|
|
|
|
|
|
|
|
git init "$REPO" &&
|
|
|
|
test_commit -C "$REPO" one &&
|
|
|
|
test_commit -C "$REPO" two &&
|
|
|
|
test_commit -C "$REPO" three &&
|
|
|
|
|
|
|
|
git init client &&
|
|
|
|
|
|
|
|
# Use protocol v2 to ensure that shallow information is sent exactly
|
|
|
|
# once by the server, since we are planning to manipulate it.
|
|
|
|
git -C "$REPO" config protocol.version 2 &&
|
|
|
|
git -C client config protocol.version 2 &&
|
|
|
|
|
|
|
|
git -C client fetch --depth=2 "$HTTPD_URL/one_time_sed/repo" master:a_branch &&
|
|
|
|
|
|
|
|
# Craft a situation in which the server sends back an unshallow request
|
|
|
|
# with an empty packfile. This is done by refetching with a shorter
|
|
|
|
# depth (to ensure that the packfile is empty), and overwriting the
|
|
|
|
# shallow line in the response with the unshallow line we want.
|
|
|
|
printf "s/0034shallow %s/0036unshallow %s/" \
|
|
|
|
"$(git -C "$REPO" rev-parse HEAD)" \
|
|
|
|
"$(git -C "$REPO" rev-parse HEAD^)" \
|
|
|
|
>"$HTTPD_ROOT_PATH/one-time-sed" &&
|
2019-01-16 19:28:15 +00:00
|
|
|
test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
|
|
|
|
fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
|
fetch-pack: write shallow, then check connectivity
When fetching, connectivity is checked after the shallow file is
updated. There are 2 issues with this: (1) the connectivity check is
only performed up to ancestors of existing refs (which is not thorough
enough if we were deepening an existing ref in the first place), and (2)
there is no rollback of the shallow file if the connectivity check
fails.
To solve (1), update the connectivity check to check the ancestry chain
completely in the case of a deepening fetch by refraining from passing
"--not --all" when invoking rev-list in connected.c.
To solve (2), have fetch_pack() perform its own connectivity check
before updating the shallow file. To support existing use cases in which
"git fetch-pack" is used to download objects without much regard as to
the connectivity of the resulting objects with respect to the existing
repository, the connectivity check is only done if necessary (that is,
the fetch is not a clone, and the fetch involves shallow/deepen
functionality). "git fetch" still performs its own connectivity check,
preserving correctness but sometimes performing redundant work. This
redundancy is mitigated by the fact that fetch_pack() reports if it has
performed a connectivity check itself, and if the transport supports
connect or stateless-connect, it will bubble up that report so that "git
fetch" knows not to perform the connectivity check in such a case.
This was noticed when a user tried to deepen an existing repository by
fetching with --no-shallow from a server that did not send all necessary
objects - the connectivity check as run by "git fetch" succeeded, but a
subsequent "git fsck" failed.
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-07-02 22:08:43 +00:00
|
|
|
master:a_branch &&
|
|
|
|
|
|
|
|
# Ensure that the one-time-sed script was used.
|
|
|
|
! test -e "$HTTPD_ROOT_PATH/one-time-sed" &&
|
|
|
|
|
|
|
|
# Ensure that the resulting repo is consistent, despite our failure to
|
|
|
|
# fetch.
|
|
|
|
git -C client fsck
|
|
|
|
'
|
|
|
|
|
2013-12-05 13:02:40 +00:00
|
|
|
test_done
|