Skip to content

Commit 5dbd767

Browse files
pcloudsgitster
authored andcommitted
receive/send-pack: support pushing from a shallow clone
Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 31c42bf commit 5dbd767

File tree

5 files changed

+146
-11
lines changed

5 files changed

+146
-11
lines changed

Documentation/technical/pack-protocol.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,9 @@ contain all the objects that the server will need to complete the new
464464
references.
465465

466466
----
467-
update-request = command-list [pack-file]
467+
update-request = *shallow command-list [pack-file]
468+
469+
shallow = PKT-LINE("shallow" SP obj-id)
468470

469471
command-list = PKT-LINE(command NUL capability-list LF)
470472
*PKT-LINE(command LF)

builtin/receive-pack.c

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ static int fix_thin = 1;
4444
static const char *head_name;
4545
static void *head_name_to_free;
4646
static int sent_capabilities;
47+
static const char *alt_shallow_file;
4748

4849
static enum deny_action parse_deny_action(const char *var, const char *value)
4950
{
@@ -190,6 +191,7 @@ struct command {
190191
const char *error_string;
191192
unsigned int skip_update:1,
192193
did_not_exist:1;
194+
int index;
193195
unsigned char old_sha1[20];
194196
unsigned char new_sha1[20];
195197
char ref_name[FLEX_ARRAY]; /* more */
@@ -688,7 +690,7 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
688690
struct command *cmd = *cmd_list;
689691

690692
while (cmd) {
691-
if (!is_null_sha1(cmd->new_sha1)) {
693+
if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
692694
hashcpy(sha1, cmd->new_sha1);
693695
*cmd_list = cmd->next;
694696
return 0;
@@ -755,7 +757,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
755757
}
756758
}
757759

758-
static struct command *read_head_info(void)
760+
static struct command *read_head_info(struct sha1_array *shallow)
759761
{
760762
struct command *commands = NULL;
761763
struct command **p = &commands;
@@ -769,6 +771,14 @@ static struct command *read_head_info(void)
769771
line = packet_read_line(0, &len);
770772
if (!line)
771773
break;
774+
775+
if (len == 48 && !prefixcmp(line, "shallow ")) {
776+
if (get_sha1_hex(line + 8, old_sha1))
777+
die("protocol error: expected shallow sha, got '%s'", line + 8);
778+
sha1_array_append(shallow, old_sha1);
779+
continue;
780+
}
781+
772782
if (len < 83 ||
773783
line[40] != ' ' ||
774784
line[81] != ' ' ||
@@ -820,7 +830,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
820830

821831
static const char *pack_lockfile;
822832

823-
static const char *unpack(int err_fd)
833+
static const char *unpack(int err_fd, struct shallow_info *si)
824834
{
825835
struct pack_header hdr;
826836
struct argv_array av = ARGV_ARRAY_INIT;
@@ -844,6 +854,11 @@ static const char *unpack(int err_fd)
844854
"--pack_header=%"PRIu32",%"PRIu32,
845855
ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
846856

857+
if (si->nr_ours || si->nr_theirs) {
858+
alt_shallow_file = setup_temporary_shallow(si->shallow);
859+
argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL);
860+
}
861+
847862
memset(&child, 0, sizeof(child));
848863
if (ntohl(hdr.hdr_entries) < unpack_limit) {
849864
argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL);
@@ -889,26 +904,62 @@ static const char *unpack(int err_fd)
889904
return NULL;
890905
}
891906

892-
static const char *unpack_with_sideband(void)
907+
static const char *unpack_with_sideband(struct shallow_info *si)
893908
{
894909
struct async muxer;
895910
const char *ret;
896911

897912
if (!use_sideband)
898-
return unpack(0);
913+
return unpack(0, si);
899914

900915
memset(&muxer, 0, sizeof(muxer));
901916
muxer.proc = copy_to_sideband;
902917
muxer.in = -1;
903918
if (start_async(&muxer))
904919
return NULL;
905920

906-
ret = unpack(muxer.in);
921+
ret = unpack(muxer.in, si);
907922

908923
finish_async(&muxer);
909924
return ret;
910925
}
911926

927+
static void update_shallow_info(struct command *commands,
928+
struct shallow_info *si,
929+
struct sha1_array *ref)
930+
{
931+
struct command *cmd;
932+
int *ref_status;
933+
remove_nonexistent_theirs_shallow(si);
934+
/* XXX remove_nonexistent_ours_in_pack() */
935+
if (!si->nr_ours && !si->nr_theirs)
936+
return;
937+
938+
for (cmd = commands; cmd; cmd = cmd->next) {
939+
if (is_null_sha1(cmd->new_sha1))
940+
continue;
941+
sha1_array_append(ref, cmd->new_sha1);
942+
cmd->index = ref->nr - 1;
943+
}
944+
si->ref = ref;
945+
946+
ref_status = xmalloc(sizeof(*ref_status) * ref->nr);
947+
assign_shallow_commits_to_refs(si, NULL, ref_status);
948+
for (cmd = commands; cmd; cmd = cmd->next) {
949+
if (is_null_sha1(cmd->new_sha1))
950+
continue;
951+
if (ref_status[cmd->index]) {
952+
cmd->error_string = "shallow update not allowed";
953+
cmd->skip_update = 1;
954+
}
955+
}
956+
if (alt_shallow_file && *alt_shallow_file) {
957+
unlink(alt_shallow_file);
958+
alt_shallow_file = NULL;
959+
}
960+
free(ref_status);
961+
}
962+
912963
static void report(struct command *commands, const char *unpack_status)
913964
{
914965
struct command *cmd;
@@ -950,6 +1001,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
9501001
int i;
9511002
char *dir = NULL;
9521003
struct command *commands;
1004+
struct sha1_array shallow = SHA1_ARRAY_INIT;
1005+
struct sha1_array ref = SHA1_ARRAY_INIT;
1006+
struct shallow_info si;
9531007

9541008
packet_trace_identity("receive-pack");
9551009

@@ -1006,11 +1060,14 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
10061060
if (advertise_refs)
10071061
return 0;
10081062

1009-
if ((commands = read_head_info()) != NULL) {
1063+
if ((commands = read_head_info(&shallow)) != NULL) {
10101064
const char *unpack_status = NULL;
10111065

1012-
if (!delete_only(commands))
1013-
unpack_status = unpack_with_sideband();
1066+
prepare_shallow_info(&si, &shallow);
1067+
if (!delete_only(commands)) {
1068+
unpack_status = unpack_with_sideband(&si);
1069+
update_shallow_info(commands, &si, &ref);
1070+
}
10141071
execute_commands(commands, unpack_status);
10151072
if (pack_lockfile)
10161073
unlink_or_warn(pack_lockfile);
@@ -1027,8 +1084,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
10271084
}
10281085
if (auto_update_server_info)
10291086
update_server_info(0);
1087+
clear_shallow_info(&si);
10301088
}
10311089
if (use_sideband)
10321090
packet_flush(1);
1091+
sha1_array_clear(&shallow);
1092+
sha1_array_clear(&ref);
10331093
return 0;
10341094
}

builtin/send-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
208208
(send_all && args.send_mirror))
209209
usage(send_pack_usage);
210210

211-
if (is_repository_shallow())
211+
if (is_repository_shallow() && args.stateless_rpc)
212212
die("attempt to push from a shallow repository");
213213

214214
if (remote_name) {

send-pack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ int send_pack(struct send_pack_args *args,
214214
return 0;
215215
}
216216

217+
if (!args->dry_run)
218+
advertise_shallow_grafts(out);
219+
217220
/*
218221
* Finally, tell the other end!
219222
*/

t/t5538-push-shallow.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/bin/sh
2+
3+
test_description='push from/to 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+
git config --global transfer.fsckObjects true &&
15+
commit 1 &&
16+
commit 2 &&
17+
commit 3 &&
18+
commit 4 &&
19+
(
20+
git init full-abc &&
21+
cd full-abc &&
22+
commit a &&
23+
commit b &&
24+
commit c
25+
) &&
26+
git clone --no-local --depth=2 .git shallow &&
27+
git --git-dir=shallow/.git log --format=%s >actual &&
28+
cat <<EOF >expect &&
29+
4
30+
3
31+
EOF
32+
test_cmp expect actual &&
33+
git clone --no-local --depth=2 full-abc/.git shallow2 &&
34+
git --git-dir=shallow2/.git log --format=%s >actual &&
35+
cat <<EOF >expect &&
36+
c
37+
b
38+
EOF
39+
test_cmp expect actual
40+
'
41+
42+
test_expect_success 'push from shallow clone' '
43+
(
44+
cd shallow &&
45+
commit 5 &&
46+
git push ../.git +master:refs/remotes/shallow/master
47+
) &&
48+
git log --format=%s shallow/master >actual &&
49+
git fsck &&
50+
cat <<EOF >expect &&
51+
5
52+
4
53+
3
54+
2
55+
1
56+
EOF
57+
test_cmp expect actual
58+
'
59+
60+
test_expect_success 'push from shallow clone, with grafted roots' '
61+
(
62+
cd shallow2 &&
63+
test_must_fail git push ../.git +master:refs/remotes/shallow2/master 2>err &&
64+
grep "shallow2/master.*shallow update not allowed" err
65+
) &&
66+
test_must_fail git rev-parse shallow2/master &&
67+
git fsck
68+
'
69+
70+
test_done

0 commit comments

Comments
 (0)