Merge branch 'maint'
[gitweb.git] / builtin / receive-pack.c
index af429e101777f1c5df09734433278cfb075e978c..8c9e91e78c95a4a3d581aae59d0f00bc95b2c942 100644 (file)
@@ -11,6 +11,7 @@
 #include "transport.h"
 #include "string-list.h"
 #include "sha1-array.h"
+#include "connected.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
@@ -25,7 +26,8 @@ static int deny_deletes;
 static int deny_non_fast_forwards;
 static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
 static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
-static int receive_fsck_objects;
+static int receive_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
@@ -35,6 +37,7 @@ static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static const char *head_name;
+static void *head_name_to_free;
 static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -79,6 +82,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "transfer.fsckobjects") == 0) {
+               transfer_fsck_objects = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "receive.denycurrentbranch")) {
                deny_current_branch = parse_deny_action(var, value);
                return 0;
@@ -107,7 +115,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static void show_ref(const char *path, const unsigned char *sha1)
 {
        if (sent_capabilities)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@@ -117,10 +125,9 @@ static int show_ref(const char *path, const unsigned char *sha1, int flag, void
                             " report-status delete-refs side-band-64k",
                             prefer_ofs_delta ? " ofs-delta" : "");
        sent_capabilities = 1;
-       return 0;
 }
 
-static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
 {
        path = strip_namespace(path);
        /*
@@ -133,21 +140,40 @@ static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, vo
         */
        if (!path)
                path = ".have";
-       return show_ref(path, sha1, flag, cb_data);
+       show_ref(path, sha1);
+       return 0;
+}
+
+static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
+{
+       show_ref(".have", sha1);
+}
+
+static void collect_one_alternate_ref(const struct ref *ref, void *data)
+{
+       struct sha1_array *sa = data;
+       sha1_array_append(sa, ref->old_sha1);
 }
 
 static void write_head_info(void)
 {
+       struct sha1_array sa = SHA1_ARRAY_INIT;
+       for_each_alternate_ref(collect_one_alternate_ref, &sa);
+       sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
+       sha1_array_clear(&sa);
        for_each_ref(show_ref_cb, NULL);
        if (!sent_capabilities)
-               show_ref("capabilities^{}", null_sha1, 0, NULL);
+               show_ref("capabilities^{}", null_sha1);
 
+       /* EOF */
+       packet_flush(1);
 }
 
 struct command {
        struct command *next;
        const char *error_string;
-       unsigned int skip_update;
+       unsigned int skip_update:1,
+                    did_not_exist:1;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char ref_name[FLEX_ARRAY]; /* more */
@@ -257,6 +283,7 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
 
 struct receive_hook_feed_state {
        struct command *cmd;
+       int skip_broken;
        struct strbuf buf;
 };
 
@@ -265,7 +292,8 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
        struct receive_hook_feed_state *state = state_;
        struct command *cmd = state->cmd;
 
-       while (cmd && cmd->error_string)
+       while (cmd &&
+              state->skip_broken && (cmd->error_string || cmd->did_not_exist))
                cmd = cmd->next;
        if (!cmd)
                return -1; /* EOF */
@@ -281,13 +309,15 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
        return 0;
 }
 
-static int run_receive_hook(struct command *commands, const char *hook_name)
+static int run_receive_hook(struct command *commands, const char *hook_name,
+                           int skip_broken)
 {
        struct receive_hook_feed_state state;
        int status;
 
        strbuf_init(&state.buf, 0);
        state.cmd = commands;
+       state.skip_broken = skip_broken;
        if (feed_receive_hook(&state, NULL, NULL))
                return 0;
        state.cmd = commands;
@@ -389,7 +419,7 @@ static const char *update(struct command *cmd)
        struct ref_lock *lock;
 
        /* only refs/... are allowed */
-       if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
+       if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
                rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
@@ -478,8 +508,13 @@ static const char *update(struct command *cmd)
 
        if (is_null_sha1(new_sha1)) {
                if (!parse_object(old_sha1)) {
-                       rp_warning("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
+                       if (ref_exists(name)) {
+                               rp_warning("Allowing deletion of corrupt ref.");
+                       } else {
+                               rp_warning("Deleting a non-existent ref.");
+                               cmd->did_not_exist = 1;
+                       }
                }
                if (delete_ref(namespaced_name, old_sha1, 0)) {
                        rp_error("failed to delete %s", name);
@@ -510,7 +545,7 @@ static void run_update_post_hook(struct command *commands)
        struct child_process proc;
 
        for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
-               if (cmd->error_string)
+               if (cmd->error_string || cmd->did_not_exist)
                        continue;
                argc++;
        }
@@ -521,7 +556,7 @@ static void run_update_post_hook(struct command *commands)
 
        for (argc = 1, cmd = commands; cmd; cmd = cmd->next) {
                char *p;
-               if (cmd->error_string)
+               if (cmd->error_string || cmd->did_not_exist)
                        continue;
                p = xmalloc(strlen(cmd->ref_name) + 1);
                strcpy(p, cmd->ref_name);
@@ -554,7 +589,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
        int flag;
 
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
-       dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+       dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
        strbuf_release(&buf);
 
        if (!(flag & REF_ISSYMREF))
@@ -612,6 +647,48 @@ static void check_aliased_updates(struct command *commands)
        string_list_clear(&ref_list, 0);
 }
 
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+{
+       struct command **cmd_list = cb_data;
+       struct command *cmd = *cmd_list;
+
+       if (!cmd || is_null_sha1(cmd->new_sha1))
+               return -1; /* end of list */
+       *cmd_list = NULL; /* this returns only one */
+       hashcpy(sha1, cmd->new_sha1);
+       return 0;
+}
+
+static void set_connectivity_errors(struct command *commands)
+{
+       struct command *cmd;
+
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               struct command *singleton = cmd;
+               if (!check_everything_connected(command_singleton_iterator,
+                                               0, &singleton))
+                       continue;
+               cmd->error_string = "missing necessary objects";
+       }
+}
+
+static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+{
+       struct command **cmd_list = cb_data;
+       struct command *cmd = *cmd_list;
+
+       while (cmd) {
+               if (!is_null_sha1(cmd->new_sha1)) {
+                       hashcpy(sha1, cmd->new_sha1);
+                       *cmd_list = cmd->next;
+                       return 0;
+               }
+               cmd = cmd->next;
+       }
+       *cmd_list = NULL;
+       return -1; /* end of list */
+}
+
 static void execute_commands(struct command *commands, const char *unpacker_error)
 {
        struct command *cmd;
@@ -623,7 +700,12 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
                return;
        }
 
-       if (run_receive_hook(commands, pre_receive_hook)) {
+       cmd = commands;
+       if (check_everything_connected(iterate_receive_command_list,
+                                      0, &cmd))
+               set_connectivity_errors(commands);
+
+       if (run_receive_hook(commands, pre_receive_hook, 0)) {
                for (cmd = commands; cmd; cmd = cmd->next)
                        cmd->error_string = "pre-receive hook declined";
                return;
@@ -631,7 +713,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
 
        check_aliased_updates(commands);
 
-       head_name = resolve_ref("HEAD", sha1, 0, NULL);
+       free(head_name_to_free);
+       head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
 
        for (cmd = commands; cmd; cmd = cmd->next)
                if (!cmd->skip_update)
@@ -707,6 +790,11 @@ static const char *unpack(void)
        struct pack_header hdr;
        const char *hdr_err;
        char hdr_arg[38];
+       int fsck_objects = (receive_fsck_objects >= 0
+                           ? receive_fsck_objects
+                           : transfer_fsck_objects >= 0
+                           ? transfer_fsck_objects
+                           : 0);
 
        hdr_err = parse_pack_header(&hdr);
        if (hdr_err)
@@ -719,7 +807,7 @@ static const char *unpack(void)
                int code, i = 0;
                const char *unpacker[4];
                unpacker[i++] = "unpack-objects";
-               if (receive_fsck_objects)
+               if (fsck_objects)
                        unpacker[i++] = "--strict";
                unpacker[i++] = hdr_arg;
                unpacker[i++] = NULL;
@@ -739,7 +827,7 @@ static const char *unpack(void)
 
                keeper[i++] = "index-pack";
                keeper[i++] = "--stdin";
-               if (receive_fsck_objects)
+               if (fsck_objects)
                        keeper[i++] = "--strict";
                keeper[i++] = "--fix-thin";
                keeper[i++] = hdr_arg;
@@ -798,25 +886,6 @@ static int delete_only(struct command *commands)
        return 1;
 }
 
-static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
-{
-       add_extra_ref(".have", sha1, 0);
-}
-
-static void collect_one_alternate_ref(const struct ref *ref, void *data)
-{
-       struct sha1_array *sa = data;
-       sha1_array_append(sa, ref->old_sha1);
-}
-
-static void add_alternate_refs(void)
-{
-       struct sha1_array sa = SHA1_ARRAY_INIT;
-       for_each_alternate_ref(collect_one_alternate_ref, &sa);
-       sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
-       sha1_array_clear(&sa);
-}
-
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
        int advertise_refs = 0;
@@ -866,12 +935,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                unpack_limit = receive_unpack_limit;
 
        if (advertise_refs || !stateless_rpc) {
-               add_alternate_refs();
                write_head_info();
-               clear_extra_refs();
-
-               /* EOF */
-               packet_flush(1);
        }
        if (advertise_refs)
                return 0;
@@ -886,7 +950,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        unlink_or_warn(pack_lockfile);
                if (report_status)
                        report(commands, unpack_status);
-               run_receive_hook(commands, post_receive_hook);
+               run_receive_hook(commands, post_receive_hook, 1);
                run_update_post_hook(commands);
                if (auto_gc) {
                        const char *argv_gc_auto[] = {