receive/send-pack: support pushing from a shallow clone

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 5dbd767601812209273ae007a97b7c9305dc6cfd
5 files changed +146 -11
Documentation/technical/pack-protocol.txt
+3 -1
@@ -464,7 +464,9 @@ contain all the objects that the server will need to complete the new
464 references.
465
466 ----
467 - update-request = command-list [pack-file]
467 + update-request = *shallow command-list [pack-file]
468 +
469 + shallow = PKT-LINE("shallow" SP obj-id)
470
471 command-list = PKT-LINE(command NUL capability-list LF)
472 *PKT-LINE(command LF)
builtin/receive-pack.c
+69 -9
@@ -44,6 +44,7 @@ static int fix_thin = 1;
44 static const char *head_name;
45 static void *head_name_to_free;
46 static int sent_capabilities;
47 +static const char *alt_shallow_file;
48
49 static enum deny_action parse_deny_action(const char *var, const char *value)
50 {
@@ -190,6 +191,7 @@ struct command {
191 const char *error_string;
192 unsigned int skip_update:1,
193 did_not_exist:1;
194 + int index;
195 unsigned char old_sha1[20];
196 unsigned char new_sha1[20];
197 char ref_name[FLEX_ARRAY]; /* more */
@@ -688,7 +690,7 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
690 struct command *cmd = *cmd_list;
691
692 while (cmd) {
691 - if (!is_null_sha1(cmd->new_sha1)) {
693 + if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
694 hashcpy(sha1, cmd->new_sha1);
695 *cmd_list = cmd->next;
696 return 0;
@@ -755,7 +757,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
757 }
758 }
759
758 -static struct command *read_head_info(void)
760 +static struct command *read_head_info(struct sha1_array *shallow)
761 {
762 struct command *commands = NULL;
763 struct command **p = &commands;
@@ -769,6 +771,14 @@ static struct command *read_head_info(void)
771 line = packet_read_line(0, &len);
772 if (!line)
773 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 +
782 if (len < 83 ||
783 line[40] != ' ' ||
784 line[81] != ' ' ||
@@ -820,7 +830,7 @@ static const char *parse_pack_header(struct pack_header *hdr)
830
831 static const char *pack_lockfile;
832
823 -static const char *unpack(int err_fd)
833 +static const char *unpack(int err_fd, struct shallow_info *si)
834 {
835 struct pack_header hdr;
836 struct argv_array av = ARGV_ARRAY_INIT;
@@ -844,6 +854,11 @@ static const char *unpack(int err_fd)
854 "--pack_header=%"PRIu32",%"PRIu32,
855 ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
856
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 +
862 memset(&child, 0, sizeof(child));
863 if (ntohl(hdr.hdr_entries) < unpack_limit) {
864 argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL);
@@ -889,13 +904,13 @@ static const char *unpack(int err_fd)
904 return NULL;
905 }
906
892 -static const char *unpack_with_sideband(void)
907 +static const char *unpack_with_sideband(struct shallow_info *si)
908 {
909 struct async muxer;
910 const char *ret;
911
912 if (!use_sideband)
898 - return unpack(0);
913 + return unpack(0, si);
914
915 memset(&muxer, 0, sizeof(muxer));
916 muxer.proc = copy_to_sideband;
@@ -903,12 +918,48 @@ static const char *unpack_with_sideband(void)
918 if (start_async(&muxer))
919 return NULL;
920
906 - ret = unpack(muxer.in);
921 + ret = unpack(muxer.in, si);
922
923 finish_async(&muxer);
924 return ret;
925 }
926
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 +
963 static void report(struct command *commands, const char *unpack_status)
964 {
965 struct command *cmd;
@@ -950,6 +1001,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
1001 int i;
1002 char *dir = NULL;
1003 struct command *commands;
1004 + struct sha1_array shallow = SHA1_ARRAY_INIT;
1005 + struct sha1_array ref = SHA1_ARRAY_INIT;
1006 + struct shallow_info si;
1007
1008 packet_trace_identity("receive-pack");
1009
@@ -1006,11 +1060,14 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
1060 if (advertise_refs)
1061 return 0;
1062
1009 - if ((commands = read_head_info()) != NULL) {
1063 + if ((commands = read_head_info(&shallow)) != NULL) {
1064 const char *unpack_status = NULL;
1065
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 + }
1071 execute_commands(commands, unpack_status);
1072 if (pack_lockfile)
1073 unlink_or_warn(pack_lockfile);
@@ -1027,8 +1084,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
1084 }
1085 if (auto_update_server_info)
1086 update_server_info(0);
1087 + clear_shallow_info(&si);
1088 }
1089 if (use_sideband)
1090 packet_flush(1);
1091 + sha1_array_clear(&shallow);
1092 + sha1_array_clear(&ref);
1093 return 0;
1094 }
builtin/send-pack.c
+1 -1
@@ -208,7 +208,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
208 (send_all && args.send_mirror))
209 usage(send_pack_usage);
210
211 - if (is_repository_shallow())
211 + if (is_repository_shallow() && args.stateless_rpc)
212 die("attempt to push from a shallow repository");
213
214 if (remote_name) {
send-pack.c
+3
@@ -214,6 +214,9 @@ int send_pack(struct send_pack_args *args,
214 return 0;
215 }
216
217 + if (!args->dry_run)
218 + advertise_shallow_grafts(out);
219 +
220 /*
221 * Finally, tell the other end!
222 */
t/t5538-push-shallow.sh new
+70
@@ -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