Skip to content

Commit

Permalink
receive-pack: receive.denyDeleteCurrent
Browse files Browse the repository at this point in the history
This is a companion patch to the recent 3d95d92 (receive-pack: explain
what to do when push updates the current branch, 2009-01-31).

Deleting the current branch from a remote will result in the next clone
from it not check out anything, among other things.  It also is one of the
cause that makes remotes/origin/HEAD a dangling symbolic ref.  This patch
still allows the traditional behaviour but with a big warning, and promises
that the default will change to 'refuse' in a future release.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Junio C Hamano committed Feb 11, 2009
1 parent ba19a80 commit 747ca24
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 11 deletions.
72 changes: 61 additions & 11 deletions builtin-receive-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ enum deny_action {
static int deny_deletes = 0;
static int deny_non_fast_forwards = 0;
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_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
static int report_status;
static const char *head_name;

static char capabilities[] = " report-status delete-refs ";
static int capabilities_sent;
Expand Down Expand Up @@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}

if (strcmp(var, "receive.denydeletecurrent") == 0) {
deny_delete_current = parse_deny_action(var, value);
return 0;
}

return git_default_config(var, value, cb);
}

Expand Down Expand Up @@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd)

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)
if (!head_name)
return 0;
return !strcmp(head, ref);
return !strcmp(head_name, ref);
}

static char *warn_unconfigured_deny_msg[] = {
Expand Down Expand Up @@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
warning(warn_unconfigured_deny_msg[i]);
}

static char *warn_unconfigured_deny_delete_current_msg[] = {
"Deleting the current branch can cause confusion by making the next",
"'git clone' not check out any file.",
"",
"You can set 'receive.denyDeleteCurrent' configuration variable to",
"'refuse' in the remote repository to disallow deleting the current",
"branch.",
"",
"You can set it to 'ignore' to allow such a delete without a warning.",
"",
"To make this warning message less loud, you can set it to 'warn'.",
"",
"Note that the default will change in a future version of git",
"to refuse deleting the current branch unless you have the",
"configuration variable set to either 'ignore' or 'warn'."
};

static void warn_unconfigured_deny_delete_current(void)
{
int i;
for (i = 0;
i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
i++)
warning(warn_unconfigured_deny_delete_current_msg[i]);
}

static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
Expand Down Expand Up @@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
"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 (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
error("denying ref deletion for %s", name);
return "deletion prohibited";
}

if (!strcmp(name, head_name)) {
switch (deny_delete_current) {
case DENY_IGNORE:
break;
case DENY_WARN:
case DENY_UNCONFIGURED:
if (deny_delete_current == DENY_UNCONFIGURED)
warn_unconfigured_deny_delete_current();
warning("deleting the current branch");
break;
case DENY_REFUSE:
error("refusing to delete the current branch: %s", name);
return "deletion of the current branch prohibited";
}
}
}

if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
!is_null_sha1(old_sha1) &&
!prefixcmp(name, "refs/heads/")) {
Expand Down Expand Up @@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
static void execute_commands(const char *unpacker_error)
{
struct command *cmd = commands;
unsigned char sha1[20];

if (unpacker_error) {
while (cmd) {
Expand All @@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
return;
}

head_name = resolve_ref("HEAD", sha1, 0, NULL);

while (cmd) {
cmd->error_string = update(cmd);
cmd = cmd->next;
Expand Down
13 changes: 13 additions & 0 deletions t/t5400-send-pack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,17 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
test "$parent_head" = "$child_head"
'

test_expect_success 'warn pushing to delete current branch' '
rewound_push_setup &&
(
cd child &&
git send-pack ../parent :refs/heads/master 2>errs
) &&
grep "warning: to refuse deleting" child/errs &&
(
cd parent &&
test_must_fail git rev-parse --verify master
)
'

test_done

0 comments on commit 747ca24

Please sign in to comment.