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>
Junio C Hamano committed
Feb 8, 2009 at 22:31 UTC
747ca2455a8dd4e255358f5f96773f84259408c3
2 files changed
+74
-11
builtin-receive-pack.c
+61
-11
@@ -21,11 +21,13 @@ enum deny_action {
21
static int deny_deletes = 0;
22
static int deny_non_fast_forwards = 0;
23
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
24
+static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
25
static int receive_fsck_objects;
26
static int receive_unpack_limit = -1;
27
static int transfer_unpack_limit = -1;
28
static int unpack_limit = 100;
29
static int report_status;
30
+static const char *head_name;
31
32
static char capabilities[] = " report-status delete-refs ";
33
static int capabilities_sent;
@@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
79
return 0;
80
}
81
82
+ if (strcmp(var, "receive.denydeletecurrent") == 0) {
83
+ deny_delete_current = parse_deny_action(var, value);
84
+ return 0;
85
+ }
86
+
87
return git_default_config(var, value, cb);
88
}
89
@@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd)
210
211
static int is_ref_checked_out(const char *ref)
212
{
206
- unsigned char sha1[20];
207
- const char *head;
208
-
213
if (is_bare_repository())
214
return 0;
215
212
- head = resolve_ref("HEAD", sha1, 0, NULL);
213
- if (!head)
216
+ if (!head_name)
217
return 0;
215
- return !strcmp(head, ref);
218
+ return !strcmp(head_name, ref);
219
}
220
221
static char *warn_unconfigured_deny_msg[] = {
@@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
247
warning(warn_unconfigured_deny_msg[i]);
248
}
249
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
+
276
static const char *update(struct command *cmd)
277
{
278
const char *name = cmd->ref_name;
@@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
307
"but I can't find it!", sha1_to_hex(new_sha1));
308
return "bad pack";
309
}
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
+ }
332
}
333
+
334
if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
335
!is_null_sha1(old_sha1) &&
336
!prefixcmp(name, "refs/heads/")) {
@@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
424
static void execute_commands(const char *unpacker_error)
425
{
426
struct command *cmd = commands;
427
+ unsigned char sha1[20];
428
429
if (unpacker_error) {
430
while (cmd) {
@@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
442
return;
443
}
444
445
+ head_name = resolve_ref("HEAD", sha1, 0, NULL);
446
+
447
while (cmd) {
448
cmd->error_string = update(cmd);
449
cmd = cmd->next;
t/t5400-send-pack.sh
+13
@@ -190,4 +190,17 @@ test_expect_success 'pushing wildcard refspecs respects forcing' '
190
test "$parent_head" = "$child_head"
191
'
192
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
+
206
test_done