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>
Nguyễn Thái Ngọc Duy committed
Dec 5, 2013 at 20:02 UTC
4820a33baa963c4559736d7a1c4c35f8dcb37293
5 files changed
+176
-5
builtin/fetch.c
+9
@@ -405,6 +405,8 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
405
struct ref **rm = cb_data;
406
struct ref *ref = *rm;
407
408
+ while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
409
+ ref = ref->next;
410
if (!ref)
411
return -1; /* end of the list */
412
*rm = ref->next;
@@ -451,6 +453,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
453
struct ref *ref = NULL;
454
const char *merge_status_marker = "";
455
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
+
463
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
464
if (!commit)
465
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
fetch-pack.c
+30
-2
@@ -854,7 +854,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
854
if (args->depth > 0)
855
setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
856
NULL);
857
- else if (args->cloning && si->shallow && si->shallow->nr)
857
+ else if (si->nr_ours || si->nr_theirs)
858
alternate_shallow_file = setup_temporary_shallow(si->shallow);
859
else
860
alternate_shallow_file = NULL;
@@ -930,8 +930,11 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
930
}
931
932
static void update_shallow(struct fetch_pack_args *args,
933
+ struct ref **sought, int nr_sought,
934
struct shallow_info *si)
935
{
936
+ struct sha1_array ref = SHA1_ARRAY_INIT;
937
+ int *status;
938
int i;
939
940
if (args->depth > 0 && alternate_shallow_file) {
@@ -978,6 +981,31 @@ static void update_shallow(struct fetch_pack_args *args,
981
sha1_array_clear(&extra);
982
return;
983
}
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);
1009
}
1010
1011
struct ref *fetch_pack(struct fetch_pack_args *args,
@@ -1003,7 +1031,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
1031
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
1032
&si, pack_lockfile);
1033
reprepare_packed_git();
1006
- update_shallow(args, &si);
1034
+ update_shallow(args, sought, nr_sought, &si);
1035
clear_shallow_info(&si);
1036
return ref_cpy;
1037
}
remote.h
+1
@@ -109,6 +109,7 @@ struct ref {
109
REF_STATUS_REJECT_FETCH_FIRST,
110
REF_STATUS_REJECT_NEEDS_FORCE,
111
REF_STATUS_REJECT_STALE,
112
+ REF_STATUS_REJECT_SHALLOW,
113
REF_STATUS_UPTODATE,
114
REF_STATUS_REMOTE_REJECT,
115
REF_STATUS_EXPECTING_REPORT
t/t5537-fetch-shallow.sh
new
+128
@@ -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
@@ -515,7 +515,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
515
get_remote_heads(data->fd[0], NULL, 0, &refs,
516
for_push ? REF_NORMAL : 0,
517
&data->extra_have,
518
- transport->cloning ? &data->shallow : NULL);
518
+ &data->shallow);
519
data->got_remote_heads = 1;
520
521
return refs;
@@ -547,8 +547,7 @@ static int fetch_refs_via_pack(struct transport *transport,
547
if (!data->got_remote_heads) {
548
connect_setup(transport, 0, 0);
549
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
550
- NULL,
551
- transport->cloning ? &data->shallow : NULL);
550
+ NULL, &data->shallow);
551
data->got_remote_heads = 1;
552
}
553
@@ -720,6 +719,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
719
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
720
"stale info", porcelain);
721
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;
726
case REF_STATUS_REMOTE_REJECT:
727
print_ref_status('!', "[remote rejected]", ref,
728
ref->deletion ? NULL : ref->peer_ref,
@@ -815,6 +818,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
818
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL, NULL);
819
data->got_remote_heads = 1;
820
}
821
+ if (data->shallow.nr)
822
+ die("pushing to a shallow repository is not supported");
823
824
memset(&args, 0, sizeof(args));
825
args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);