Skip to content

Commit 4820a33

Browse files
pcloudsgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent beea415 commit 4820a33

File tree

5 files changed

+176
-5
lines changed

5 files changed

+176
-5
lines changed

builtin/fetch.c

+9
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
405405
struct ref **rm = cb_data;
406406
struct ref *ref = *rm;
407407

408+
while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
409+
ref = ref->next;
408410
if (!ref)
409411
return -1; /* end of the list */
410412
*rm = ref->next;
@@ -451,6 +453,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
451453
struct ref *ref = NULL;
452454
const char *merge_status_marker = "";
453455

456+
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
457+
if (want_status == FETCH_HEAD_MERGE)
458+
warning(_("reject %s because shallow roots are not allowed to be updated"),
459+
rm->peer_ref ? rm->peer_ref->name : rm->name);
460+
continue;
461+
}
462+
454463
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
455464
if (!commit)
456465
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;

fetch-pack.c

+30-2
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
854854
if (args->depth > 0)
855855
setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
856856
NULL);
857-
else if (args->cloning && si->shallow && si->shallow->nr)
857+
else if (si->nr_ours || si->nr_theirs)
858858
alternate_shallow_file = setup_temporary_shallow(si->shallow);
859859
else
860860
alternate_shallow_file = NULL;
@@ -930,8 +930,11 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
930930
}
931931

932932
static void update_shallow(struct fetch_pack_args *args,
933+
struct ref **sought, int nr_sought,
933934
struct shallow_info *si)
934935
{
936+
struct sha1_array ref = SHA1_ARRAY_INIT;
937+
int *status;
935938
int i;
936939

937940
if (args->depth > 0 && alternate_shallow_file) {
@@ -978,6 +981,31 @@ static void update_shallow(struct fetch_pack_args *args,
978981
sha1_array_clear(&extra);
979982
return;
980983
}
984+
985+
if (!si->nr_ours && !si->nr_theirs)
986+
return;
987+
988+
remove_nonexistent_theirs_shallow(si);
989+
/* XXX remove_nonexistent_ours_in_pack() */
990+
if (!si->nr_ours && !si->nr_theirs)
991+
return;
992+
for (i = 0; i < nr_sought; i++)
993+
sha1_array_append(&ref, sought[i]->old_sha1);
994+
si->ref = &ref;
995+
996+
/*
997+
* remote is also shallow, check what ref is safe to update
998+
* without updating .git/shallow
999+
*/
1000+
status = xcalloc(nr_sought, sizeof(*status));
1001+
assign_shallow_commits_to_refs(si, NULL, status);
1002+
if (si->nr_ours || si->nr_theirs) {
1003+
for (i = 0; i < nr_sought; i++)
1004+
if (status[i])
1005+
sought[i]->status = REF_STATUS_REJECT_SHALLOW;
1006+
}
1007+
free(status);
1008+
sha1_array_clear(&ref);
9811009
}
9821010

9831011
struct ref *fetch_pack(struct fetch_pack_args *args,
@@ -1003,7 +1031,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
10031031
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
10041032
&si, pack_lockfile);
10051033
reprepare_packed_git();
1006-
update_shallow(args, &si);
1034+
update_shallow(args, sought, nr_sought, &si);
10071035
clear_shallow_info(&si);
10081036
return ref_cpy;
10091037
}

remote.h

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct ref {
109109
REF_STATUS_REJECT_FETCH_FIRST,
110110
REF_STATUS_REJECT_NEEDS_FORCE,
111111
REF_STATUS_REJECT_STALE,
112+
REF_STATUS_REJECT_SHALLOW,
112113
REF_STATUS_UPTODATE,
113114
REF_STATUS_REMOTE_REJECT,
114115
REF_STATUS_EXPECTING_REPORT

t/t5537-fetch-shallow.sh

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/bin/sh
2+
3+
test_description='fetch/clone from a shallow clone'
4+
5+
. ./test-lib.sh
6+
7+
commit() {
8+
echo "$1" >tracked &&
9+
git add tracked &&
10+
git commit -m "$1"
11+
}
12+
13+
test_expect_success 'setup' '
14+
commit 1 &&
15+
commit 2 &&
16+
commit 3 &&
17+
commit 4 &&
18+
git config --global transfer.fsckObjects true
19+
'
20+
21+
test_expect_success 'setup shallow clone' '
22+
git clone --no-local --depth=2 .git shallow &&
23+
git --git-dir=shallow/.git log --format=%s >actual &&
24+
cat <<EOF >expect &&
25+
4
26+
3
27+
EOF
28+
test_cmp expect actual
29+
'
30+
31+
test_expect_success 'clone from shallow clone' '
32+
git clone --no-local shallow shallow2 &&
33+
(
34+
cd shallow2 &&
35+
git fsck &&
36+
git log --format=%s >actual &&
37+
cat <<EOF >expect &&
38+
4
39+
3
40+
EOF
41+
test_cmp expect actual
42+
)
43+
'
44+
45+
test_expect_success 'fetch from shallow clone' '
46+
(
47+
cd shallow &&
48+
commit 5
49+
) &&
50+
(
51+
cd shallow2 &&
52+
git fetch &&
53+
git fsck &&
54+
git log --format=%s origin/master >actual &&
55+
cat <<EOF >expect &&
56+
5
57+
4
58+
3
59+
EOF
60+
test_cmp expect actual
61+
)
62+
'
63+
64+
test_expect_success 'fetch --depth from shallow clone' '
65+
(
66+
cd shallow &&
67+
commit 6
68+
) &&
69+
(
70+
cd shallow2 &&
71+
git fetch --depth=2 &&
72+
git fsck &&
73+
git log --format=%s origin/master >actual &&
74+
cat <<EOF >expect &&
75+
6
76+
5
77+
EOF
78+
test_cmp expect actual
79+
)
80+
'
81+
82+
test_expect_success 'fetch something upstream has but hidden by clients shallow boundaries' '
83+
# the blob "1" is available in .git but hidden by the
84+
# shallow2/.git/shallow and it should be resent
85+
! git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null &&
86+
echo 1 >1.t &&
87+
git add 1.t &&
88+
git commit -m add-1-back &&
89+
(
90+
cd shallow2 &&
91+
git fetch ../.git +refs/heads/master:refs/remotes/top/master &&
92+
git fsck &&
93+
git log --format=%s top/master >actual &&
94+
cat <<EOF >expect &&
95+
add-1-back
96+
4
97+
3
98+
EOF
99+
test_cmp expect actual
100+
) &&
101+
git --git-dir=shallow2/.git cat-file blob `echo 1|git hash-object --stdin` >/dev/null
102+
103+
'
104+
105+
test_expect_success 'fetch that requires changes in .git/shallow is filtered' '
106+
(
107+
cd shallow &&
108+
git checkout --orphan no-shallow &&
109+
commit no-shallow
110+
) &&
111+
git init notshallow &&
112+
(
113+
cd notshallow &&
114+
git fetch ../shallow/.git refs/heads/*:refs/remotes/shallow/*&&
115+
git for-each-ref --format="%(refname)" >actual.refs &&
116+
cat <<EOF >expect.refs &&
117+
refs/remotes/shallow/no-shallow
118+
EOF
119+
test_cmp expect.refs actual.refs &&
120+
git log --format=%s shallow/no-shallow >actual &&
121+
cat <<EOF >expect &&
122+
no-shallow
123+
EOF
124+
test_cmp expect actual
125+
)
126+
'
127+
128+
test_done

transport.c

+8-3
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
515515
get_remote_heads(data->fd[0], NULL, 0, &refs,
516516
for_push ? REF_NORMAL : 0,
517517
&data->extra_have,
518-
transport->cloning ? &data->shallow : NULL);
518+
&data->shallow);
519519
data->got_remote_heads = 1;
520520

521521
return refs;
@@ -547,8 +547,7 @@ static int fetch_refs_via_pack(struct transport *transport,
547547
if (!data->got_remote_heads) {
548548
connect_setup(transport, 0, 0);
549549
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
550-
NULL,
551-
transport->cloning ? &data->shallow : NULL);
550+
NULL, &data->shallow);
552551
data->got_remote_heads = 1;
553552
}
554553

@@ -720,6 +719,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
720719
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
721720
"stale info", porcelain);
722721
break;
722+
case REF_STATUS_REJECT_SHALLOW:
723+
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
724+
"new shallow roots not allowed", porcelain);
725+
break;
723726
case REF_STATUS_REMOTE_REJECT:
724727
print_ref_status('!', "[remote rejected]", ref,
725728
ref->deletion ? NULL : ref->peer_ref,
@@ -815,6 +818,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
815818
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL, NULL);
816819
data->got_remote_heads = 1;
817820
}
821+
if (data->shallow.nr)
822+
die("pushing to a shallow repository is not supported");
818823

819824
memset(&args, 0, sizeof(args));
820825
args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);

0 commit comments

Comments
 (0)