receive-pack: allow pushes that update .git/shallow

The basic 8 steps to update .git/shallow does not fully apply here because the user may choose to accept just a few refs (while fetch always accepts all refs). The steps are modified a bit. 1-6. same as before. After calling assign_shallow_commits_to_refs at step 6, each shallow commit has a bitmap that marks all refs that require it. 7. mark all "ours" shallow commits that are reachable from any refs. We will need to do the original step 7 on them later. 8. go over all shallow commit bitmaps, mark refs that require new shallow commits. 9. setup a strict temporary shallow file to plug all the holes, even if it may cut some of our history short. This file is used by all hooks. The hooks could use --shallow-file=$GIT_DIR/shallow to overcome this and reach everything in current repo. 10. go over the new refs one by one. For each ref, do the reachability test if it needs a shallow commit on the list from step 7. Remove it if it's reachable from our refs. Gather all required shallow commits, run check_everything_connected() with the new ref, then install them to .git/shallow. This mode is disabled by default and can be turned on with receive.shallowupdate 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 0a1bc12b6e401825f009ac8bb14fc438f77e2d9f
5 files changed +201 -13
Documentation/config.txt
+4
@@ -2026,6 +2026,10 @@ receive.updateserverinfo::
2026 If set to true, git-receive-pack will run git-update-server-info
2027 after receiving data from git-push and updating refs.
2028
2029 +receive.shallowupdate::
2030 + If set to true, .git/shallow can be updated when new refs
2031 + require new shallow roots. Otherwise those refs are rejected.
2032 +
2033 remote.pushdefault::
2034 The remote to push to by default. Overrides
2035 `branch.<name>.remote` for all branches, and is overridden by
builtin/receive-pack.c
+150 -13
@@ -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 int shallow_update;
48 static const char *alt_shallow_file;
49
50 static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -123,6 +124,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
124 return 0;
125 }
126
127 + if (strcmp(var, "receive.shallowupdate") == 0) {
128 + shallow_update = git_config_bool(var, value);
129 + return 0;
130 + }
131 +
132 return git_default_config(var, value, cb);
133 }
134
@@ -423,7 +429,46 @@ static void refuse_unconfigured_deny_delete_current(void)
429 rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
430 }
431
426 -static const char *update(struct command *cmd)
432 +static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
433 +static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
434 +{
435 + static struct lock_file shallow_lock;
436 + struct sha1_array extra = SHA1_ARRAY_INIT;
437 + const char *alt_file;
438 + uint32_t mask = 1 << (cmd->index % 32);
439 + int i;
440 +
441 + trace_printf_key("GIT_TRACE_SHALLOW",
442 + "shallow: update_shallow_ref %s\n", cmd->ref_name);
443 + for (i = 0; i < si->shallow->nr; i++)
444 + if (si->used_shallow[i] &&
445 + (si->used_shallow[i][cmd->index / 32] & mask) &&
446 + !delayed_reachability_test(si, i))
447 + sha1_array_append(&extra, si->shallow->sha1[i]);
448 +
449 + setup_alternate_shallow(&shallow_lock, &alt_file, &extra);
450 + if (check_shallow_connected(command_singleton_iterator,
451 + 0, cmd, alt_file)) {
452 + rollback_lock_file(&shallow_lock);
453 + sha1_array_clear(&extra);
454 + return -1;
455 + }
456 +
457 + commit_lock_file(&shallow_lock);
458 +
459 + /*
460 + * Make sure setup_alternate_shallow() for the next ref does
461 + * not lose these new roots..
462 + */
463 + for (i = 0; i < extra.nr; i++)
464 + register_shallow(extra.sha1[i]);
465 +
466 + si->shallow_ref[cmd->index] = 0;
467 + sha1_array_clear(&extra);
468 + return 0;
469 +}
470 +
471 +static const char *update(struct command *cmd, struct shallow_info *si)
472 {
473 const char *name = cmd->ref_name;
474 struct strbuf namespaced_name_buf = STRBUF_INIT;
@@ -531,6 +576,10 @@ static const char *update(struct command *cmd)
576 return NULL; /* good */
577 }
578 else {
579 + if (shallow_update && si->shallow_ref[cmd->index] &&
580 + update_shallow_ref(cmd, si))
581 + return "shallow error";
582 +
583 lock = lock_any_ref_for_update(namespaced_name, old_sha1,
584 0, NULL);
585 if (!lock) {
@@ -671,12 +720,16 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
720 return 0;
721 }
722
674 -static void set_connectivity_errors(struct command *commands)
723 +static void set_connectivity_errors(struct command *commands,
724 + struct shallow_info *si)
725 {
726 struct command *cmd;
727
728 for (cmd = commands; cmd; cmd = cmd->next) {
729 struct command *singleton = cmd;
730 + if (shallow_update && si->shallow_ref[cmd->index])
731 + /* to be checked in update_shallow_ref() */
732 + continue;
733 if (!check_everything_connected(command_singleton_iterator,
734 0, &singleton))
735 continue;
@@ -684,18 +737,26 @@ static void set_connectivity_errors(struct command *commands)
737 }
738 }
739
740 +struct iterate_data {
741 + struct command *cmds;
742 + struct shallow_info *si;
743 +};
744 +
745 static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
746 {
689 - struct command **cmd_list = cb_data;
747 + struct iterate_data *data = cb_data;
748 + struct command **cmd_list = &data->cmds;
749 struct command *cmd = *cmd_list;
750
692 - while (cmd) {
751 + for (; cmd; cmd = cmd->next) {
752 + if (shallow_update && data->si->shallow_ref[cmd->index])
753 + /* to be checked in update_shallow_ref() */
754 + continue;
755 if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
756 hashcpy(sha1, cmd->new_sha1);
757 *cmd_list = cmd->next;
758 return 0;
759 }
698 - cmd = cmd->next;
760 }
761 *cmd_list = NULL;
762 return -1; /* end of list */
@@ -715,10 +776,14 @@ static void reject_updates_to_hidden(struct command *commands)
776 }
777 }
778
718 -static void execute_commands(struct command *commands, const char *unpacker_error)
779 +static void execute_commands(struct command *commands,
780 + const char *unpacker_error,
781 + struct shallow_info *si)
782 {
783 + int checked_connectivity;
784 struct command *cmd;
785 unsigned char sha1[20];
786 + struct iterate_data data;
787
788 if (unpacker_error) {
789 for (cmd = commands; cmd; cmd = cmd->next)
@@ -726,10 +791,10 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
791 return;
792 }
793
729 - cmd = commands;
730 - if (check_everything_connected(iterate_receive_command_list,
731 - 0, &cmd))
732 - set_connectivity_errors(commands);
794 + data.cmds = commands;
795 + data.si = si;
796 + if (check_everything_connected(iterate_receive_command_list, 0, &data))
797 + set_connectivity_errors(commands, si);
798
799 reject_updates_to_hidden(commands);
800
@@ -746,6 +811,7 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
811 free(head_name_to_free);
812 head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
813
814 + checked_connectivity = 1;
815 for (cmd = commands; cmd; cmd = cmd->next) {
816 if (cmd->error_string)
817 continue;
@@ -753,7 +819,22 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
819 if (cmd->skip_update)
820 continue;
821
756 - cmd->error_string = update(cmd);
822 + cmd->error_string = update(cmd, si);
823 + if (shallow_update && !cmd->error_string &&
824 + si->shallow_ref[cmd->index]) {
825 + error("BUG: connectivity check has not been run on ref %s",
826 + cmd->ref_name);
827 + checked_connectivity = 0;
828 + }
829 + }
830 +
831 + if (shallow_update) {
832 + if (!checked_connectivity)
833 + error("BUG: run 'git fsck' for safety.\n"
834 + "If there are errors, try to remove "
835 + "the reported refs above");
836 + if (alt_shallow_file && *alt_shallow_file)
837 + unlink(alt_shallow_file);
838 }
839 }
840
@@ -924,6 +1005,53 @@ static const char *unpack_with_sideband(struct shallow_info *si)
1005 return ret;
1006 }
1007
1008 +static void prepare_shallow_update(struct command *commands,
1009 + struct shallow_info *si)
1010 +{
1011 + int i, j, k, bitmap_size = (si->ref->nr + 31) / 32;
1012 +
1013 + si->used_shallow = xmalloc(sizeof(*si->used_shallow) *
1014 + si->shallow->nr);
1015 + assign_shallow_commits_to_refs(si, si->used_shallow, NULL);
1016 +
1017 + si->need_reachability_test =
1018 + xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test));
1019 + si->reachable =
1020 + xcalloc(si->shallow->nr, sizeof(*si->reachable));
1021 + si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref));
1022 +
1023 + for (i = 0; i < si->nr_ours; i++)
1024 + si->need_reachability_test[si->ours[i]] = 1;
1025 +
1026 + for (i = 0; i < si->shallow->nr; i++) {
1027 + if (!si->used_shallow[i])
1028 + continue;
1029 + for (j = 0; j < bitmap_size; j++) {
1030 + if (!si->used_shallow[i][j])
1031 + continue;
1032 + si->need_reachability_test[i]++;
1033 + for (k = 0; k < 32; k++)
1034 + if (si->used_shallow[i][j] & (1 << k))
1035 + si->shallow_ref[j * 32 + k]++;
1036 + }
1037 +
1038 + /*
1039 + * true for those associated with some refs and belong
1040 + * in "ours" list aka "step 7 not done yet"
1041 + */
1042 + si->need_reachability_test[i] =
1043 + si->need_reachability_test[i] > 1;
1044 + }
1045 +
1046 + /*
1047 + * keep hooks happy by forcing a temporary shallow file via
1048 + * env variable because we can't add --shallow-file to every
1049 + * command. check_everything_connected() will be done with
1050 + * true .git/shallow though.
1051 + */
1052 + setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
1053 +}
1054 +
1055 static void update_shallow_info(struct command *commands,
1056 struct shallow_info *si,
1057 struct sha1_array *ref)
@@ -932,8 +1060,10 @@ static void update_shallow_info(struct command *commands,
1060 int *ref_status;
1061 remove_nonexistent_theirs_shallow(si);
1062 /* XXX remove_nonexistent_ours_in_pack() */
935 - if (!si->nr_ours && !si->nr_theirs)
1063 + if (!si->nr_ours && !si->nr_theirs) {
1064 + shallow_update = 0;
1065 return;
1066 + }
1067
1068 for (cmd = commands; cmd; cmd = cmd->next) {
1069 if (is_null_sha1(cmd->new_sha1))
@@ -943,6 +1073,11 @@ static void update_shallow_info(struct command *commands,
1073 }
1074 si->ref = ref;
1075
1076 + if (shallow_update) {
1077 + prepare_shallow_update(commands, si);
1078 + return;
1079 + }
1080 +
1081 ref_status = xmalloc(sizeof(*ref_status) * ref->nr);
1082 assign_shallow_commits_to_refs(si, NULL, ref_status);
1083 for (cmd = commands; cmd; cmd = cmd->next) {
@@ -1064,11 +1199,13 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
1199 const char *unpack_status = NULL;
1200
1201 prepare_shallow_info(&si, &shallow);
1202 + if (!si.nr_ours && !si.nr_theirs)
1203 + shallow_update = 0;
1204 if (!delete_only(commands)) {
1205 unpack_status = unpack_with_sideband(&si);
1206 update_shallow_info(commands, &si, &ref);
1207 }
1071 - execute_commands(commands, unpack_status);
1208 + execute_commands(commands, unpack_status, &si);
1209 if (pack_lockfile)
1210 unlink_or_warn(pack_lockfile);
1211 if (report_status)
commit.h
+9
@@ -216,6 +216,14 @@ struct shallow_info {
216 int *ours, nr_ours;
217 int *theirs, nr_theirs;
218 struct sha1_array *ref;
219 +
220 + /* for receive-pack */
221 + uint32_t **used_shallow;
222 + int *need_reachability_test;
223 + int *reachable;
224 + int *shallow_ref;
225 + struct commit **commits;
226 + int nr_commits;
227 };
228
229 extern void prepare_shallow_info(struct shallow_info *, struct sha1_array *);
@@ -226,6 +234,7 @@ extern void remove_nonexistent_ours_in_pack(struct shallow_info *,
234 extern void assign_shallow_commits_to_refs(struct shallow_info *info,
235 uint32_t **used,
236 int *ref_status);
237 +extern int delayed_reachability_test(struct shallow_info *si, int c);
238
239 int is_descendant_of(struct commit *, struct commit_list *);
240 int in_merge_bases(struct commit *, struct commit *);
shallow.c
+23
@@ -617,3 +617,26 @@ static void post_assign_shallow(struct shallow_info *info,
617
618 free(ca.commits);
619 }
620 +
621 +/* (Delayed) step 7, reachability test at commit level */
622 +int delayed_reachability_test(struct shallow_info *si, int c)
623 +{
624 + if (si->need_reachability_test[c]) {
625 + struct commit *commit = lookup_commit(si->shallow->sha1[c]);
626 +
627 + if (!si->commits) {
628 + struct commit_array ca;
629 + memset(&ca, 0, sizeof(ca));
630 + head_ref(add_ref, &ca);
631 + for_each_ref(add_ref, &ca);
632 + si->commits = ca.commits;
633 + si->nr_commits = ca.nr;
634 + }
635 +
636 + si->reachable[c] = in_merge_bases_many(commit,
637 + si->nr_commits,
638 + si->commits);
639 + si->need_reachability_test[c] = 0;
640 + }
641 + return si->reachable[c];
642 +}
t/t5538-push-shallow.sh
+15
@@ -67,4 +67,19 @@ test_expect_success 'push from shallow clone, with grafted roots' '
67 git fsck
68 '
69
70 +test_expect_success 'add new shallow root with receive.updateshallow on' '
71 + test_config receive.shallowupdate true &&
72 + (
73 + cd shallow2 &&
74 + git push ../.git +master:refs/remotes/shallow2/master
75 + ) &&
76 + git log --format=%s shallow2/master >actual &&
77 + git fsck &&
78 + cat <<EOF >expect &&
79 +c
80 +b
81 +EOF
82 + test_cmp expect actual
83 +'
84 +
85 test_done