fetch: support fetching from a shallow repository

This patch just put together pieces from the 8 steps patch. We stop at
step 7 and reject refs that require new shallow commits.

Note that, by rejecting refs that require new shallow commits, we
leave dangling objects in the repo, which become "object islands" by
the next "git fetch" of the same source.

If the first fetch our "ours" set is zero and we do practically
nothing at step 7, "ours" is full at the next fetch and we may need to
walk through commits for reachability test. Room for improvement.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy 2013-12-05 20:02:40 +07:00 коммит произвёл Junio C Hamano
Родитель beea4152d9
Коммит 4820a33baa
5 изменённых файлов: 176 добавлений и 5 удалений

Просмотреть файл

@ -405,6 +405,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
struct ref **rm = cb_data;
struct ref *ref = *rm;
while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
ref = ref->next;
if (!ref)
return -1; /* end of the list */
*rm = ref->next;
@ -451,6 +453,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
struct ref *ref = NULL;
const char *merge_status_marker = "";
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
if (want_status == FETCH_HEAD_MERGE)
warning(_("reject %s because shallow roots are not allowed to be updated"),
rm->peer_ref ? rm->peer_ref->name : rm->name);
continue;
}
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;

Просмотреть файл

@ -854,7 +854,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
if (args->depth > 0)
setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
NULL);
else if (args->cloning && si->shallow && si->shallow->nr)
else if (si->nr_ours || si->nr_theirs)
alternate_shallow_file = setup_temporary_shallow(si->shallow);
else
alternate_shallow_file = NULL;
@ -930,8 +930,11 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
}
static void update_shallow(struct fetch_pack_args *args,
struct ref **sought, int nr_sought,
struct shallow_info *si)
{
struct sha1_array ref = SHA1_ARRAY_INIT;
int *status;
int i;
if (args->depth > 0 && alternate_shallow_file) {
@ -978,6 +981,31 @@ static void update_shallow(struct fetch_pack_args *args,
sha1_array_clear(&extra);
return;
}
if (!si->nr_ours && !si->nr_theirs)
return;
remove_nonexistent_theirs_shallow(si);
/* XXX remove_nonexistent_ours_in_pack() */
if (!si->nr_ours && !si->nr_theirs)
return;
for (i = 0; i < nr_sought; i++)
sha1_array_append(&ref, sought[i]->old_sha1);
si->ref = &ref;
/*
* remote is also shallow, check what ref is safe to update
* without updating .git/shallow
*/
status = xcalloc(nr_sought, sizeof(*status));
assign_shallow_commits_to_refs(si, NULL, status);
if (si->nr_ours || si->nr_theirs) {
for (i = 0; i < nr_sought; i++)
if (status[i])
sought[i]->status = REF_STATUS_REJECT_SHALLOW;
}
free(status);
sha1_array_clear(&ref);
}
struct ref *fetch_pack(struct fetch_pack_args *args,
@ -1003,7 +1031,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
&si, pack_lockfile);
reprepare_packed_git();
update_shallow(args, &si);
update_shallow(args, sought, nr_sought, &si);
clear_shallow_info(&si);
return ref_cpy;
}

Просмотреть файл

@ -109,6 +109,7 @@ struct ref {
REF_STATUS_REJECT_FETCH_FIRST,
REF_STATUS_REJECT_NEEDS_FORCE,
REF_STATUS_REJECT_STALE,
REF_STATUS_REJECT_SHALLOW,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
REF_STATUS_EXPECTING_REPORT

128
t/t5537-fetch-shallow.sh Executable file
Просмотреть файл

@ -0,0 +1,128 @@
#!/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
)
'
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
! git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null &&
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
) &&
git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null
'
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
)
'
test_done

Просмотреть файл

@ -515,7 +515,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
get_remote_heads(data->fd[0], NULL, 0, &refs,
for_push ? REF_NORMAL : 0,
&data->extra_have,
transport->cloning ? &data->shallow : NULL);
&data->shallow);
data->got_remote_heads = 1;
return refs;
@ -547,8 +547,7 @@ static int fetch_refs_via_pack(struct transport *transport,
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
NULL,
transport->cloning ? &data->shallow : NULL);
NULL, &data->shallow);
data->got_remote_heads = 1;
}
@ -720,6 +719,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"stale info", porcelain);
break;
case REF_STATUS_REJECT_SHALLOW:
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"new shallow roots not allowed", porcelain);
break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
@ -815,6 +818,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL, NULL);
data->got_remote_heads = 1;
}
if (data->shallow.nr)
die("pushing to a shallow repository is not supported");
memset(&args, 0, sizeof(args));
args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);