receive-pack: receive.denyDeleteCurrent
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>
This commit is contained in:
parent
ba19a808aa
commit
747ca2455a
@ -21,11 +21,13 @@ enum deny_action {
|
|||||||
static int deny_deletes = 0;
|
static int deny_deletes = 0;
|
||||||
static int deny_non_fast_forwards = 0;
|
static int deny_non_fast_forwards = 0;
|
||||||
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
|
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;
|
||||||
static int receive_unpack_limit = -1;
|
static int receive_unpack_limit = -1;
|
||||||
static int transfer_unpack_limit = -1;
|
static int transfer_unpack_limit = -1;
|
||||||
static int unpack_limit = 100;
|
static int unpack_limit = 100;
|
||||||
static int report_status;
|
static int report_status;
|
||||||
|
static const char *head_name;
|
||||||
|
|
||||||
static char capabilities[] = " report-status delete-refs ";
|
static char capabilities[] = " report-status delete-refs ";
|
||||||
static int capabilities_sent;
|
static int capabilities_sent;
|
||||||
@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
|
|||||||
return 0;
|
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);
|
return git_default_config(var, value, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd)
|
|||||||
|
|
||||||
static int is_ref_checked_out(const char *ref)
|
static int is_ref_checked_out(const char *ref)
|
||||||
{
|
{
|
||||||
unsigned char sha1[20];
|
|
||||||
const char *head;
|
|
||||||
|
|
||||||
if (is_bare_repository())
|
if (is_bare_repository())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
head = resolve_ref("HEAD", sha1, 0, NULL);
|
if (!head_name)
|
||||||
if (!head)
|
|
||||||
return 0;
|
return 0;
|
||||||
return !strcmp(head, ref);
|
return !strcmp(head_name, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *warn_unconfigured_deny_msg[] = {
|
static char *warn_unconfigured_deny_msg[] = {
|
||||||
@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
|
|||||||
warning(warn_unconfigured_deny_msg[i]);
|
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)
|
static const char *update(struct command *cmd)
|
||||||
{
|
{
|
||||||
const char *name = cmd->ref_name;
|
const char *name = cmd->ref_name;
|
||||||
@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
|
|||||||
"but I can't find it!", sha1_to_hex(new_sha1));
|
"but I can't find it!", sha1_to_hex(new_sha1));
|
||||||
return "bad pack";
|
return "bad pack";
|
||||||
}
|
}
|
||||||
if (deny_deletes && is_null_sha1(new_sha1) &&
|
|
||||||
!is_null_sha1(old_sha1) &&
|
if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
|
||||||
!prefixcmp(name, "refs/heads/")) {
|
if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
|
||||||
error("denying ref deletion for %s", name);
|
error("denying ref deletion for %s", name);
|
||||||
return "deletion prohibited";
|
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) &&
|
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
|
||||||
!is_null_sha1(old_sha1) &&
|
!is_null_sha1(old_sha1) &&
|
||||||
!prefixcmp(name, "refs/heads/")) {
|
!prefixcmp(name, "refs/heads/")) {
|
||||||
@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
|
|||||||
static void execute_commands(const char *unpacker_error)
|
static void execute_commands(const char *unpacker_error)
|
||||||
{
|
{
|
||||||
struct command *cmd = commands;
|
struct command *cmd = commands;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
|
||||||
if (unpacker_error) {
|
if (unpacker_error) {
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
head_name = resolve_ref("HEAD", sha1, 0, NULL);
|
||||||
|
|
||||||
while (cmd) {
|
while (cmd) {
|
||||||
cmd->error_string = update(cmd);
|
cmd->error_string = update(cmd);
|
||||||
cmd = cmd->next;
|
cmd = cmd->next;
|
||||||
|
@ -190,4 +190,17 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
|
|||||||
test "$parent_head" = "$child_head"
|
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
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user