Skip to content

Commit 747ca24

Browse files
committed
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 <[email protected]>
1 parent ba19a80 commit 747ca24

File tree

2 files changed

+74
-11
lines changed

2 files changed

+74
-11
lines changed

builtin-receive-pack.c

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ enum deny_action {
2121
static int deny_deletes = 0;
2222
static int deny_non_fast_forwards = 0;
2323
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
24+
static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
2425
static int receive_fsck_objects;
2526
static int receive_unpack_limit = -1;
2627
static int transfer_unpack_limit = -1;
2728
static int unpack_limit = 100;
2829
static int report_status;
30+
static const char *head_name;
2931

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

82+
if (strcmp(var, "receive.denydeletecurrent") == 0) {
83+
deny_delete_current = parse_deny_action(var, value);
84+
return 0;
85+
}
86+
8087
return git_default_config(var, value, cb);
8188
}
8289

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

204211
static int is_ref_checked_out(const char *ref)
205212
{
206-
unsigned char sha1[20];
207-
const char *head;
208-
209213
if (is_bare_repository())
210214
return 0;
211215

212-
head = resolve_ref("HEAD", sha1, 0, NULL);
213-
if (!head)
216+
if (!head_name)
214217
return 0;
215-
return !strcmp(head, ref);
218+
return !strcmp(head_name, ref);
216219
}
217220

218221
static char *warn_unconfigured_deny_msg[] = {
@@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
244247
warning(warn_unconfigured_deny_msg[i]);
245248
}
246249

250+
static char *warn_unconfigured_deny_delete_current_msg[] = {
251+
"Deleting the current branch can cause confusion by making the next",
252+
"'git clone' not check out any file.",
253+
"",
254+
"You can set 'receive.denyDeleteCurrent' configuration variable to",
255+
"'refuse' in the remote repository to disallow deleting the current",
256+
"branch.",
257+
"",
258+
"You can set it to 'ignore' to allow such a delete without a warning.",
259+
"",
260+
"To make this warning message less loud, you can set it to 'warn'.",
261+
"",
262+
"Note that the default will change in a future version of git",
263+
"to refuse deleting the current branch unless you have the",
264+
"configuration variable set to either 'ignore' or 'warn'."
265+
};
266+
267+
static void warn_unconfigured_deny_delete_current(void)
268+
{
269+
int i;
270+
for (i = 0;
271+
i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
272+
i++)
273+
warning(warn_unconfigured_deny_delete_current_msg[i]);
274+
}
275+
247276
static const char *update(struct command *cmd)
248277
{
249278
const char *name = cmd->ref_name;
@@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
278307
"but I can't find it!", sha1_to_hex(new_sha1));
279308
return "bad pack";
280309
}
281-
if (deny_deletes && is_null_sha1(new_sha1) &&
282-
!is_null_sha1(old_sha1) &&
283-
!prefixcmp(name, "refs/heads/")) {
284-
error("denying ref deletion for %s", name);
285-
return "deletion prohibited";
310+
311+
if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
312+
if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
313+
error("denying ref deletion for %s", name);
314+
return "deletion prohibited";
315+
}
316+
317+
if (!strcmp(name, head_name)) {
318+
switch (deny_delete_current) {
319+
case DENY_IGNORE:
320+
break;
321+
case DENY_WARN:
322+
case DENY_UNCONFIGURED:
323+
if (deny_delete_current == DENY_UNCONFIGURED)
324+
warn_unconfigured_deny_delete_current();
325+
warning("deleting the current branch");
326+
break;
327+
case DENY_REFUSE:
328+
error("refusing to delete the current branch: %s", name);
329+
return "deletion of the current branch prohibited";
330+
}
331+
}
286332
}
333+
287334
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
288335
!is_null_sha1(old_sha1) &&
289336
!prefixcmp(name, "refs/heads/")) {
@@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
377424
static void execute_commands(const char *unpacker_error)
378425
{
379426
struct command *cmd = commands;
427+
unsigned char sha1[20];
380428

381429
if (unpacker_error) {
382430
while (cmd) {
@@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
394442
return;
395443
}
396444

445+
head_name = resolve_ref("HEAD", sha1, 0, NULL);
446+
397447
while (cmd) {
398448
cmd->error_string = update(cmd);
399449
cmd = cmd->next;

t/t5400-send-pack.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,17 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
190190
test "$parent_head" = "$child_head"
191191
'
192192

193+
test_expect_success 'warn pushing to delete current branch' '
194+
rewound_push_setup &&
195+
(
196+
cd child &&
197+
git send-pack ../parent :refs/heads/master 2>errs
198+
) &&
199+
grep "warning: to refuse deleting" child/errs &&
200+
(
201+
cd parent &&
202+
test_must_fail git rev-parse --verify master
203+
)
204+
'
205+
193206
test_done

0 commit comments

Comments
 (0)