Code

config.mak.in: define paths without trailing slash
[git.git] / builtin-receive-pack.c
index 45e3cd90fd476cdb0a32e5de27739b18e060e031..596dfe92233e9ad07cebfa7e36a404ea909da716 100644 (file)
@@ -9,9 +9,17 @@
 #include "remote.h"
 #include "transport.h"
 
-static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
+static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
+enum deny_action {
+       DENY_IGNORE,
+       DENY_WARN,
+       DENY_REFUSE,
+};
+
+static int deny_deletes = 0;
 static int deny_non_fast_forwards = 0;
+static enum deny_action deny_current_branch = DENY_WARN;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
@@ -21,8 +29,28 @@ static int report_status;
 static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
 
+static enum deny_action parse_deny_action(const char *var, const char *value)
+{
+       if (value) {
+               if (!strcasecmp(value, "ignore"))
+                       return DENY_IGNORE;
+               if (!strcasecmp(value, "warn"))
+                       return DENY_WARN;
+               if (!strcasecmp(value, "refuse"))
+                       return DENY_REFUSE;
+       }
+       if (git_config_bool(var, value))
+               return DENY_REFUSE;
+       return DENY_IGNORE;
+}
+
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+       if (strcmp(var, "receive.denydeletes") == 0) {
+               deny_deletes = git_config_bool(var, value);
+               return 0;
+       }
+
        if (strcmp(var, "receive.denynonfastforwards") == 0) {
                deny_non_fast_forwards = git_config_bool(var, value);
                return 0;
@@ -43,6 +71,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "receive.denycurrentbranch")) {
+               deny_current_branch = parse_deny_action(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -103,7 +136,7 @@ static int hook_status(int code, const char *hook_name)
        }
 }
 
-static int run_hook(const char *hook_name)
+static int run_receive_hook(const char *hook_name)
 {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
        struct command *cmd;
@@ -167,6 +200,20 @@ static int run_update_hook(struct command *cmd)
        return hook_status(run_command(&proc), update_hook);
 }
 
+static int is_ref_checked_out(const char *ref)
+{
+       unsigned char sha1[20];
+       const char *head;
+
+       if (is_bare_repository())
+               return 0;
+
+       head = resolve_ref("HEAD", sha1, 0, NULL);
+       if (!head)
+               return 0;
+       return !strcmp(head, ref);
+}
+
 static const char *update(struct command *cmd)
 {
        const char *name = cmd->ref_name;
@@ -180,11 +227,35 @@ static const char *update(struct command *cmd)
                return "funny refname";
        }
 
+       switch (deny_current_branch) {
+       case DENY_IGNORE:
+               break;
+       case DENY_WARN:
+               if (!is_ref_checked_out(name))
+                       break;
+               warning("updating the currently checked out branch; this may"
+                       " cause confusion,\n"
+                       "as the index and working tree do not reflect changes"
+                       " that are now in HEAD.");
+               break;
+       case DENY_REFUSE:
+               if (!is_ref_checked_out(name))
+                       break;
+               error("refusing to update checked out branch: %s", name);
+               return "branch is currently checked out";
+       }
+
        if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
                error("unpack should have generated %s, "
                      "but I can't find it!", sha1_to_hex(new_sha1));
                return "bad pack";
        }
+       if (deny_deletes && is_null_sha1(new_sha1) &&
+           !is_null_sha1(old_sha1) &&
+           !prefixcmp(name, "refs/heads/")) {
+               error("denying ref deletion for %s", name);
+               return "deletion prohibited";
+       }
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
            !prefixcmp(name, "refs/heads/")) {
@@ -224,7 +295,7 @@ static const char *update(struct command *cmd)
                        warning ("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
-               if (delete_ref(name, old_sha1)) {
+               if (delete_ref(name, old_sha1, 0)) {
                        error("failed to delete %s", name);
                        return "failed to delete";
                }
@@ -287,7 +358,7 @@ static void execute_commands(const char *unpacker_error)
                return;
        }
 
-       if (run_hook(pre_receive_hook)) {
+       if (run_receive_hook(pre_receive_hook)) {
                while (cmd) {
                        cmd->error_string = "pre-receive hook declined";
                        cmd = cmd->next;
@@ -466,12 +537,17 @@ static int delete_only(struct command *cmd)
 
 static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
 {
-       char *other = xstrdup(make_absolute_path(e->base));
-       size_t len = strlen(other);
+       char *other;
+       size_t len;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
 
+       e->name[-1] = '\0';
+       other = xstrdup(make_absolute_path(e->base));
+       e->name[-1] = '/';
+       len = strlen(other);
+
        while (other[len-1] == '/')
                other[--len] = '\0';
        if (len < 8 || memcmp(other + len - 8, "/objects", 8))
@@ -551,7 +627,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        unlink(pack_lockfile);
                if (report_status)
                        report(unpack_status);
-               run_hook(post_receive_hook);
+               run_receive_hook(post_receive_hook);
                run_update_post_hook(commands);
        }
        return 0;