Skip to content

Commit

Permalink
fetch: support fetching from a shallow repository
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nguyễn Thái Ngọc Duy authored and Junio C Hamano committed Dec 11, 2013
1 parent beea415 commit 4820a33
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 5 deletions.
9 changes: 9 additions & 0 deletions builtin/fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
32 changes: 30 additions & 2 deletions fetch-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
1 change: 1 addition & 0 deletions remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
128 changes: 128 additions & 0 deletions t/t5537-fetch-shallow.sh
Original file line number Diff line number Diff line change
@@ -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
11 changes: 8 additions & 3 deletions transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4820a33

Please sign in to comment.